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         if (this.primaryButtonOnly && e.button != 0) {
826             return;
827         }
828
829         if (this.isLocked()) {
830             return;
831         }
832
833         this.DDM.refreshCache(this.groups);
834
835         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
836         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
837         } else {
838             if (this.clickValidator(e)) {
839
840                 // set the initial element position
841                 this.setStartPosition();
842
843
844                 this.b4MouseDown(e);
845                 this.onMouseDown(e);
846
847                 this.DDM.handleMouseDown(e, this);
848
849                 this.DDM.stopEvent(e);
850             } else {
851
852
853             }
854         }
855     },
856
857     clickValidator: function(e) {
858         var target = e.getTarget();
859         return ( this.isValidHandleChild(target) &&
860                     (this.id == this.handleElId ||
861                         this.DDM.handleWasClicked(target, this.id)) );
862     },
863
864     /**
865      * Allows you to specify a tag name that should not start a drag operation
866      * when clicked.  This is designed to facilitate embedding links within a
867      * drag handle that do something other than start the drag.
868      * @method addInvalidHandleType
869      * @param {string} tagName the type of element to exclude
870      */
871     addInvalidHandleType: function(tagName) {
872         var type = tagName.toUpperCase();
873         this.invalidHandleTypes[type] = type;
874     },
875
876     /**
877      * Lets you to specify an element id for a child of a drag handle
878      * that should not initiate a drag
879      * @method addInvalidHandleId
880      * @param {string} id the element id of the element you wish to ignore
881      */
882     addInvalidHandleId: function(id) {
883         if (typeof id !== "string") {
884             id = Roo.id(id);
885         }
886         this.invalidHandleIds[id] = id;
887     },
888
889     /**
890      * Lets you specify a css class of elements that will not initiate a drag
891      * @method addInvalidHandleClass
892      * @param {string} cssClass the class of the elements you wish to ignore
893      */
894     addInvalidHandleClass: function(cssClass) {
895         this.invalidHandleClasses.push(cssClass);
896     },
897
898     /**
899      * Unsets an excluded tag name set by addInvalidHandleType
900      * @method removeInvalidHandleType
901      * @param {string} tagName the type of element to unexclude
902      */
903     removeInvalidHandleType: function(tagName) {
904         var type = tagName.toUpperCase();
905         // this.invalidHandleTypes[type] = null;
906         delete this.invalidHandleTypes[type];
907     },
908
909     /**
910      * Unsets an invalid handle id
911      * @method removeInvalidHandleId
912      * @param {string} id the id of the element to re-enable
913      */
914     removeInvalidHandleId: function(id) {
915         if (typeof id !== "string") {
916             id = Roo.id(id);
917         }
918         delete this.invalidHandleIds[id];
919     },
920
921     /**
922      * Unsets an invalid css class
923      * @method removeInvalidHandleClass
924      * @param {string} cssClass the class of the element(s) you wish to
925      * re-enable
926      */
927     removeInvalidHandleClass: function(cssClass) {
928         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
929             if (this.invalidHandleClasses[i] == cssClass) {
930                 delete this.invalidHandleClasses[i];
931             }
932         }
933     },
934
935     /**
936      * Checks the tag exclusion list to see if this click should be ignored
937      * @method isValidHandleChild
938      * @param {HTMLElement} node the HTMLElement to evaluate
939      * @return {boolean} true if this is a valid tag type, false if not
940      */
941     isValidHandleChild: function(node) {
942
943         var valid = true;
944         // var n = (node.nodeName == "#text") ? node.parentNode : node;
945         var nodeName;
946         try {
947             nodeName = node.nodeName.toUpperCase();
948         } catch(e) {
949             nodeName = node.nodeName;
950         }
951         valid = valid && !this.invalidHandleTypes[nodeName];
952         valid = valid && !this.invalidHandleIds[node.id];
953
954         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
955             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
956         }
957
958
959         return valid;
960
961     },
962
963     /**
964      * Create the array of horizontal tick marks if an interval was specified
965      * in setXConstraint().
966      * @method setXTicks
967      * @private
968      */
969     setXTicks: function(iStartX, iTickSize) {
970         this.xTicks = [];
971         this.xTickSize = iTickSize;
972
973         var tickMap = {};
974
975         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
976             if (!tickMap[i]) {
977                 this.xTicks[this.xTicks.length] = i;
978                 tickMap[i] = true;
979             }
980         }
981
982         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
983             if (!tickMap[i]) {
984                 this.xTicks[this.xTicks.length] = i;
985                 tickMap[i] = true;
986             }
987         }
988
989         this.xTicks.sort(this.DDM.numericSort) ;
990     },
991
992     /**
993      * Create the array of vertical tick marks if an interval was specified in
994      * setYConstraint().
995      * @method setYTicks
996      * @private
997      */
998     setYTicks: function(iStartY, iTickSize) {
999         this.yTicks = [];
1000         this.yTickSize = iTickSize;
1001
1002         var tickMap = {};
1003
1004         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1005             if (!tickMap[i]) {
1006                 this.yTicks[this.yTicks.length] = i;
1007                 tickMap[i] = true;
1008             }
1009         }
1010
1011         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1012             if (!tickMap[i]) {
1013                 this.yTicks[this.yTicks.length] = i;
1014                 tickMap[i] = true;
1015             }
1016         }
1017
1018         this.yTicks.sort(this.DDM.numericSort) ;
1019     },
1020
1021     /**
1022      * By default, the element can be dragged any place on the screen.  Use
1023      * this method to limit the horizontal travel of the element.  Pass in
1024      * 0,0 for the parameters if you want to lock the drag to the y axis.
1025      * @method setXConstraint
1026      * @param {int} iLeft the number of pixels the element can move to the left
1027      * @param {int} iRight the number of pixels the element can move to the
1028      * right
1029      * @param {int} iTickSize optional parameter for specifying that the
1030      * element
1031      * should move iTickSize pixels at a time.
1032      */
1033     setXConstraint: function(iLeft, iRight, iTickSize) {
1034         this.leftConstraint = iLeft;
1035         this.rightConstraint = iRight;
1036
1037         this.minX = this.initPageX - iLeft;
1038         this.maxX = this.initPageX + iRight;
1039         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1040
1041         this.constrainX = true;
1042     },
1043
1044     /**
1045      * Clears any constraints applied to this instance.  Also clears ticks
1046      * since they can't exist independent of a constraint at this time.
1047      * @method clearConstraints
1048      */
1049     clearConstraints: function() {
1050         this.constrainX = false;
1051         this.constrainY = false;
1052         this.clearTicks();
1053     },
1054
1055     /**
1056      * Clears any tick interval defined for this instance
1057      * @method clearTicks
1058      */
1059     clearTicks: function() {
1060         this.xTicks = null;
1061         this.yTicks = null;
1062         this.xTickSize = 0;
1063         this.yTickSize = 0;
1064     },
1065
1066     /**
1067      * By default, the element can be dragged any place on the screen.  Set
1068      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1069      * parameters if you want to lock the drag to the x axis.
1070      * @method setYConstraint
1071      * @param {int} iUp the number of pixels the element can move up
1072      * @param {int} iDown the number of pixels the element can move down
1073      * @param {int} iTickSize optional parameter for specifying that the
1074      * element should move iTickSize pixels at a time.
1075      */
1076     setYConstraint: function(iUp, iDown, iTickSize) {
1077         this.topConstraint = iUp;
1078         this.bottomConstraint = iDown;
1079
1080         this.minY = this.initPageY - iUp;
1081         this.maxY = this.initPageY + iDown;
1082         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1083
1084         this.constrainY = true;
1085
1086     },
1087
1088     /**
1089      * resetConstraints must be called if you manually reposition a dd element.
1090      * @method resetConstraints
1091      * @param {boolean} maintainOffset
1092      */
1093     resetConstraints: function() {
1094
1095
1096         // Maintain offsets if necessary
1097         if (this.initPageX || this.initPageX === 0) {
1098             // figure out how much this thing has moved
1099             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1100             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1101
1102             this.setInitPosition(dx, dy);
1103
1104         // This is the first time we have detected the element's position
1105         } else {
1106             this.setInitPosition();
1107         }
1108
1109         if (this.constrainX) {
1110             this.setXConstraint( this.leftConstraint,
1111                                  this.rightConstraint,
1112                                  this.xTickSize        );
1113         }
1114
1115         if (this.constrainY) {
1116             this.setYConstraint( this.topConstraint,
1117                                  this.bottomConstraint,
1118                                  this.yTickSize         );
1119         }
1120     },
1121
1122     /**
1123      * Normally the drag element is moved pixel by pixel, but we can specify
1124      * that it move a number of pixels at a time.  This method resolves the
1125      * location when we have it set up like this.
1126      * @method getTick
1127      * @param {int} val where we want to place the object
1128      * @param {int[]} tickArray sorted array of valid points
1129      * @return {int} the closest tick
1130      * @private
1131      */
1132     getTick: function(val, tickArray) {
1133
1134         if (!tickArray) {
1135             // If tick interval is not defined, it is effectively 1 pixel,
1136             // so we return the value passed to us.
1137             return val;
1138         } else if (tickArray[0] >= val) {
1139             // The value is lower than the first tick, so we return the first
1140             // tick.
1141             return tickArray[0];
1142         } else {
1143             for (var i=0, len=tickArray.length; i<len; ++i) {
1144                 var next = i + 1;
1145                 if (tickArray[next] && tickArray[next] >= val) {
1146                     var diff1 = val - tickArray[i];
1147                     var diff2 = tickArray[next] - val;
1148                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1149                 }
1150             }
1151
1152             // The value is larger than the last tick, so we return the last
1153             // tick.
1154             return tickArray[tickArray.length - 1];
1155         }
1156     },
1157
1158     /**
1159      * toString method
1160      * @method toString
1161      * @return {string} string representation of the dd obj
1162      */
1163     toString: function() {
1164         return ("DragDrop " + this.id);
1165     }
1166
1167 });
1168
1169 })();
1170 /*
1171  * Based on:
1172  * Ext JS Library 1.1.1
1173  * Copyright(c) 2006-2007, Ext JS, LLC.
1174  *
1175  * Originally Released Under LGPL - original licence link has changed is not relivant.
1176  *
1177  * Fork - LGPL
1178  * <script type="text/javascript">
1179  */
1180
1181
1182 /**
1183  * The drag and drop utility provides a framework for building drag and drop
1184  * applications.  In addition to enabling drag and drop for specific elements,
1185  * the drag and drop elements are tracked by the manager class, and the
1186  * interactions between the various elements are tracked during the drag and
1187  * the implementing code is notified about these important moments.
1188  */
1189
1190 // Only load the library once.  Rewriting the manager class would orphan
1191 // existing drag and drop instances.
1192 if (!Roo.dd.DragDropMgr) {
1193
1194 /**
1195  * @class Roo.dd.DragDropMgr
1196  * DragDropMgr is a singleton that tracks the element interaction for
1197  * all DragDrop items in the window.  Generally, you will not call
1198  * this class directly, but it does have helper methods that could
1199  * be useful in your DragDrop implementations.
1200  * @singleton
1201  */
1202 Roo.dd.DragDropMgr = function() {
1203
1204     var Event = Roo.EventManager;
1205
1206     return {
1207
1208         /**
1209          * Two dimensional Array of registered DragDrop objects.  The first
1210          * dimension is the DragDrop item group, the second the DragDrop
1211          * object.
1212          * @property ids
1213          * @type {string: string}
1214          * @private
1215          * @static
1216          */
1217         ids: {},
1218
1219         /**
1220          * Array of element ids defined as drag handles.  Used to determine
1221          * if the element that generated the mousedown event is actually the
1222          * handle and not the html element itself.
1223          * @property handleIds
1224          * @type {string: string}
1225          * @private
1226          * @static
1227          */
1228         handleIds: {},
1229
1230         /**
1231          * the DragDrop object that is currently being dragged
1232          * @property dragCurrent
1233          * @type DragDrop
1234          * @private
1235          * @static
1236          **/
1237         dragCurrent: null,
1238
1239         /**
1240          * the DragDrop object(s) that are being hovered over
1241          * @property dragOvers
1242          * @type Array
1243          * @private
1244          * @static
1245          */
1246         dragOvers: {},
1247
1248         /**
1249          * the X distance between the cursor and the object being dragged
1250          * @property deltaX
1251          * @type int
1252          * @private
1253          * @static
1254          */
1255         deltaX: 0,
1256
1257         /**
1258          * the Y distance between the cursor and the object being dragged
1259          * @property deltaY
1260          * @type int
1261          * @private
1262          * @static
1263          */
1264         deltaY: 0,
1265
1266         /**
1267          * Flag to determine if we should prevent the default behavior of the
1268          * events we define. By default this is true, but this can be set to
1269          * false if you need the default behavior (not recommended)
1270          * @property preventDefault
1271          * @type boolean
1272          * @static
1273          */
1274         preventDefault: true,
1275
1276         /**
1277          * Flag to determine if we should stop the propagation of the events
1278          * we generate. This is true by default but you may want to set it to
1279          * false if the html element contains other features that require the
1280          * mouse click.
1281          * @property stopPropagation
1282          * @type boolean
1283          * @static
1284          */
1285         stopPropagation: true,
1286
1287         /**
1288          * Internal flag that is set to true when drag and drop has been
1289          * intialized
1290          * @property initialized
1291          * @private
1292          * @static
1293          */
1294         initalized: false,
1295
1296         /**
1297          * All drag and drop can be disabled.
1298          * @property locked
1299          * @private
1300          * @static
1301          */
1302         locked: false,
1303
1304         /**
1305          * Called the first time an element is registered.
1306          * @method init
1307          * @private
1308          * @static
1309          */
1310         init: function() {
1311             this.initialized = true;
1312         },
1313
1314         /**
1315          * In point mode, drag and drop interaction is defined by the
1316          * location of the cursor during the drag/drop
1317          * @property POINT
1318          * @type int
1319          * @static
1320          */
1321         POINT: 0,
1322
1323         /**
1324          * In intersect mode, drag and drop interactio nis defined by the
1325          * overlap of two or more drag and drop objects.
1326          * @property INTERSECT
1327          * @type int
1328          * @static
1329          */
1330         INTERSECT: 1,
1331
1332         /**
1333          * The current drag and drop mode.  Default: POINT
1334          * @property mode
1335          * @type int
1336          * @static
1337          */
1338         mode: 0,
1339
1340         /**
1341          * Runs method on all drag and drop objects
1342          * @method _execOnAll
1343          * @private
1344          * @static
1345          */
1346         _execOnAll: function(sMethod, args) {
1347             for (var i in this.ids) {
1348                 for (var j in this.ids[i]) {
1349                     var oDD = this.ids[i][j];
1350                     if (! this.isTypeOfDD(oDD)) {
1351                         continue;
1352                     }
1353                     oDD[sMethod].apply(oDD, args);
1354                 }
1355             }
1356         },
1357
1358         /**
1359          * Drag and drop initialization.  Sets up the global event handlers
1360          * @method _onLoad
1361          * @private
1362          * @static
1363          */
1364         _onLoad: function() {
1365
1366             this.init();
1367
1368             if (!Roo.isTouch) {
1369                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1370                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1371             }
1372             Event.on(document, "touchend",   this.handleMouseUp, this, true);
1373             Event.on(document, "touchmove", this.handleMouseMove, this, true);
1374             
1375             Event.on(window,   "unload",    this._onUnload, this, true);
1376             Event.on(window,   "resize",    this._onResize, this, true);
1377             // Event.on(window,   "mouseout",    this._test);
1378
1379         },
1380
1381         /**
1382          * Reset constraints on all drag and drop objs
1383          * @method _onResize
1384          * @private
1385          * @static
1386          */
1387         _onResize: function(e) {
1388             this._execOnAll("resetConstraints", []);
1389         },
1390
1391         /**
1392          * Lock all drag and drop functionality
1393          * @method lock
1394          * @static
1395          */
1396         lock: function() { this.locked = true; },
1397
1398         /**
1399          * Unlock all drag and drop functionality
1400          * @method unlock
1401          * @static
1402          */
1403         unlock: function() { this.locked = false; },
1404
1405         /**
1406          * Is drag and drop locked?
1407          * @method isLocked
1408          * @return {boolean} True if drag and drop is locked, false otherwise.
1409          * @static
1410          */
1411         isLocked: function() { return this.locked; },
1412
1413         /**
1414          * Location cache that is set for all drag drop objects when a drag is
1415          * initiated, cleared when the drag is finished.
1416          * @property locationCache
1417          * @private
1418          * @static
1419          */
1420         locationCache: {},
1421
1422         /**
1423          * Set useCache to false if you want to force object the lookup of each
1424          * drag and drop linked element constantly during a drag.
1425          * @property useCache
1426          * @type boolean
1427          * @static
1428          */
1429         useCache: true,
1430
1431         /**
1432          * The number of pixels that the mouse needs to move after the
1433          * mousedown before the drag is initiated.  Default=3;
1434          * @property clickPixelThresh
1435          * @type int
1436          * @static
1437          */
1438         clickPixelThresh: 3,
1439
1440         /**
1441          * The number of milliseconds after the mousedown event to initiate the
1442          * drag if we don't get a mouseup event. Default=1000
1443          * @property clickTimeThresh
1444          * @type int
1445          * @static
1446          */
1447         clickTimeThresh: 350,
1448
1449         /**
1450          * Flag that indicates that either the drag pixel threshold or the
1451          * mousdown time threshold has been met
1452          * @property dragThreshMet
1453          * @type boolean
1454          * @private
1455          * @static
1456          */
1457         dragThreshMet: false,
1458
1459         /**
1460          * Timeout used for the click time threshold
1461          * @property clickTimeout
1462          * @type Object
1463          * @private
1464          * @static
1465          */
1466         clickTimeout: null,
1467
1468         /**
1469          * The X position of the mousedown event stored for later use when a
1470          * drag threshold is met.
1471          * @property startX
1472          * @type int
1473          * @private
1474          * @static
1475          */
1476         startX: 0,
1477
1478         /**
1479          * The Y position of the mousedown event stored for later use when a
1480          * drag threshold is met.
1481          * @property startY
1482          * @type int
1483          * @private
1484          * @static
1485          */
1486         startY: 0,
1487
1488         /**
1489          * Each DragDrop instance must be registered with the DragDropMgr.
1490          * This is executed in DragDrop.init()
1491          * @method regDragDrop
1492          * @param {DragDrop} oDD the DragDrop object to register
1493          * @param {String} sGroup the name of the group this element belongs to
1494          * @static
1495          */
1496         regDragDrop: function(oDD, sGroup) {
1497             if (!this.initialized) { this.init(); }
1498
1499             if (!this.ids[sGroup]) {
1500                 this.ids[sGroup] = {};
1501             }
1502             this.ids[sGroup][oDD.id] = oDD;
1503         },
1504
1505         /**
1506          * Removes the supplied dd instance from the supplied group. Executed
1507          * by DragDrop.removeFromGroup, so don't call this function directly.
1508          * @method removeDDFromGroup
1509          * @private
1510          * @static
1511          */
1512         removeDDFromGroup: function(oDD, sGroup) {
1513             if (!this.ids[sGroup]) {
1514                 this.ids[sGroup] = {};
1515             }
1516
1517             var obj = this.ids[sGroup];
1518             if (obj && obj[oDD.id]) {
1519                 delete obj[oDD.id];
1520             }
1521         },
1522
1523         /**
1524          * Unregisters a drag and drop item.  This is executed in
1525          * DragDrop.unreg, use that method instead of calling this directly.
1526          * @method _remove
1527          * @private
1528          * @static
1529          */
1530         _remove: function(oDD) {
1531             for (var g in oDD.groups) {
1532                 if (g && this.ids[g][oDD.id]) {
1533                     delete this.ids[g][oDD.id];
1534                 }
1535             }
1536             delete this.handleIds[oDD.id];
1537         },
1538
1539         /**
1540          * Each DragDrop handle element must be registered.  This is done
1541          * automatically when executing DragDrop.setHandleElId()
1542          * @method regHandle
1543          * @param {String} sDDId the DragDrop id this element is a handle for
1544          * @param {String} sHandleId the id of the element that is the drag
1545          * handle
1546          * @static
1547          */
1548         regHandle: function(sDDId, sHandleId) {
1549             if (!this.handleIds[sDDId]) {
1550                 this.handleIds[sDDId] = {};
1551             }
1552             this.handleIds[sDDId][sHandleId] = sHandleId;
1553         },
1554
1555         /**
1556          * Utility function to determine if a given element has been
1557          * registered as a drag drop item.
1558          * @method isDragDrop
1559          * @param {String} id the element id to check
1560          * @return {boolean} true if this element is a DragDrop item,
1561          * false otherwise
1562          * @static
1563          */
1564         isDragDrop: function(id) {
1565             return ( this.getDDById(id) ) ? true : false;
1566         },
1567
1568         /**
1569          * Returns the drag and drop instances that are in all groups the
1570          * passed in instance belongs to.
1571          * @method getRelated
1572          * @param {DragDrop} p_oDD the obj to get related data for
1573          * @param {boolean} bTargetsOnly if true, only return targetable objs
1574          * @return {DragDrop[]} the related instances
1575          * @static
1576          */
1577         getRelated: function(p_oDD, bTargetsOnly) {
1578             var oDDs = [];
1579             for (var i in p_oDD.groups) {
1580                 for (j in this.ids[i]) {
1581                     var dd = this.ids[i][j];
1582                     if (! this.isTypeOfDD(dd)) {
1583                         continue;
1584                     }
1585                     if (!bTargetsOnly || dd.isTarget) {
1586                         oDDs[oDDs.length] = dd;
1587                     }
1588                 }
1589             }
1590
1591             return oDDs;
1592         },
1593
1594         /**
1595          * Returns true if the specified dd target is a legal target for
1596          * the specifice drag obj
1597          * @method isLegalTarget
1598          * @param {DragDrop} the drag obj
1599          * @param {DragDrop} the target
1600          * @return {boolean} true if the target is a legal target for the
1601          * dd obj
1602          * @static
1603          */
1604         isLegalTarget: function (oDD, oTargetDD) {
1605             var targets = this.getRelated(oDD, true);
1606             for (var i=0, len=targets.length;i<len;++i) {
1607                 if (targets[i].id == oTargetDD.id) {
1608                     return true;
1609                 }
1610             }
1611
1612             return false;
1613         },
1614
1615         /**
1616          * My goal is to be able to transparently determine if an object is
1617          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1618          * returns "object", oDD.constructor.toString() always returns
1619          * "DragDrop" and not the name of the subclass.  So for now it just
1620          * evaluates a well-known variable in DragDrop.
1621          * @method isTypeOfDD
1622          * @param {Object} the object to evaluate
1623          * @return {boolean} true if typeof oDD = DragDrop
1624          * @static
1625          */
1626         isTypeOfDD: function (oDD) {
1627             return (oDD && oDD.__ygDragDrop);
1628         },
1629
1630         /**
1631          * Utility function to determine if a given element has been
1632          * registered as a drag drop handle for the given Drag Drop object.
1633          * @method isHandle
1634          * @param {String} id the element id to check
1635          * @return {boolean} true if this element is a DragDrop handle, false
1636          * otherwise
1637          * @static
1638          */
1639         isHandle: function(sDDId, sHandleId) {
1640             return ( this.handleIds[sDDId] &&
1641                             this.handleIds[sDDId][sHandleId] );
1642         },
1643
1644         /**
1645          * Returns the DragDrop instance for a given id
1646          * @method getDDById
1647          * @param {String} id the id of the DragDrop object
1648          * @return {DragDrop} the drag drop object, null if it is not found
1649          * @static
1650          */
1651         getDDById: function(id) {
1652             for (var i in this.ids) {
1653                 if (this.ids[i][id]) {
1654                     return this.ids[i][id];
1655                 }
1656             }
1657             return null;
1658         },
1659
1660         /**
1661          * Fired after a registered DragDrop object gets the mousedown event.
1662          * Sets up the events required to track the object being dragged
1663          * @method handleMouseDown
1664          * @param {Event} e the event
1665          * @param oDD the DragDrop object being dragged
1666          * @private
1667          * @static
1668          */
1669         handleMouseDown: function(e, oDD) {
1670             if(Roo.QuickTips){
1671                 Roo.QuickTips.disable();
1672             }
1673             this.currentTarget = e.getTarget();
1674
1675             this.dragCurrent = oDD;
1676
1677             var el = oDD.getEl();
1678
1679             // track start position
1680             this.startX = e.getPageX();
1681             this.startY = e.getPageY();
1682
1683             this.deltaX = this.startX - el.offsetLeft;
1684             this.deltaY = this.startY - el.offsetTop;
1685
1686             this.dragThreshMet = false;
1687
1688             this.clickTimeout = setTimeout(
1689                     function() {
1690                         var DDM = Roo.dd.DDM;
1691                         DDM.startDrag(DDM.startX, DDM.startY);
1692                     },
1693                     this.clickTimeThresh );
1694         },
1695
1696         /**
1697          * Fired when either the drag pixel threshol or the mousedown hold
1698          * time threshold has been met.
1699          * @method startDrag
1700          * @param x {int} the X position of the original mousedown
1701          * @param y {int} the Y position of the original mousedown
1702          * @static
1703          */
1704         startDrag: function(x, y) {
1705             clearTimeout(this.clickTimeout);
1706             if (this.dragCurrent) {
1707                 this.dragCurrent.b4StartDrag(x, y);
1708                 this.dragCurrent.startDrag(x, y);
1709             }
1710             this.dragThreshMet = true;
1711         },
1712
1713         /**
1714          * Internal function to handle the mouseup event.  Will be invoked
1715          * from the context of the document.
1716          * @method handleMouseUp
1717          * @param {Event} e the event
1718          * @private
1719          * @static
1720          */
1721         handleMouseUp: function(e) {
1722
1723             if(Roo.QuickTips){
1724                 Roo.QuickTips.enable();
1725             }
1726             if (! this.dragCurrent) {
1727                 return;
1728             }
1729
1730             clearTimeout(this.clickTimeout);
1731
1732             if (this.dragThreshMet) {
1733                 this.fireEvents(e, true);
1734             } else {
1735             }
1736
1737             this.stopDrag(e);
1738
1739             this.stopEvent(e);
1740         },
1741
1742         /**
1743          * Utility to stop event propagation and event default, if these
1744          * features are turned on.
1745          * @method stopEvent
1746          * @param {Event} e the event as returned by this.getEvent()
1747          * @static
1748          */
1749         stopEvent: function(e){
1750             if(this.stopPropagation) {
1751                 e.stopPropagation();
1752             }
1753
1754             if (this.preventDefault) {
1755                 e.preventDefault();
1756             }
1757         },
1758
1759         /**
1760          * Internal function to clean up event handlers after the drag
1761          * operation is complete
1762          * @method stopDrag
1763          * @param {Event} e the event
1764          * @private
1765          * @static
1766          */
1767         stopDrag: function(e) {
1768             // Fire the drag end event for the item that was dragged
1769             if (this.dragCurrent) {
1770                 if (this.dragThreshMet) {
1771                     this.dragCurrent.b4EndDrag(e);
1772                     this.dragCurrent.endDrag(e);
1773                 }
1774
1775                 this.dragCurrent.onMouseUp(e);
1776             }
1777
1778             this.dragCurrent = null;
1779             this.dragOvers = {};
1780         },
1781
1782         /**
1783          * Internal function to handle the mousemove event.  Will be invoked
1784          * from the context of the html element.
1785          *
1786          * @TODO figure out what we can do about mouse events lost when the
1787          * user drags objects beyond the window boundary.  Currently we can
1788          * detect this in internet explorer by verifying that the mouse is
1789          * down during the mousemove event.  Firefox doesn't give us the
1790          * button state on the mousemove event.
1791          * @method handleMouseMove
1792          * @param {Event} e the event
1793          * @private
1794          * @static
1795          */
1796         handleMouseMove: function(e) {
1797             if (! this.dragCurrent) {
1798                 return true;
1799             }
1800
1801             // var button = e.which || e.button;
1802
1803             // check for IE mouseup outside of page boundary
1804             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1805                 this.stopEvent(e);
1806                 return this.handleMouseUp(e);
1807             }
1808
1809             if (!this.dragThreshMet) {
1810                 var diffX = Math.abs(this.startX - e.getPageX());
1811                 var diffY = Math.abs(this.startY - e.getPageY());
1812                 if (diffX > this.clickPixelThresh ||
1813                             diffY > this.clickPixelThresh) {
1814                     this.startDrag(this.startX, this.startY);
1815                 }
1816             }
1817
1818             if (this.dragThreshMet) {
1819                 this.dragCurrent.b4Drag(e);
1820                 this.dragCurrent.onDrag(e);
1821                 if(!this.dragCurrent.moveOnly){
1822                     this.fireEvents(e, false);
1823                 }
1824             }
1825
1826             this.stopEvent(e);
1827
1828             return true;
1829         },
1830
1831         /**
1832          * Iterates over all of the DragDrop elements to find ones we are
1833          * hovering over or dropping on
1834          * @method fireEvents
1835          * @param {Event} e the event
1836          * @param {boolean} isDrop is this a drop op or a mouseover op?
1837          * @private
1838          * @static
1839          */
1840         fireEvents: function(e, isDrop) {
1841             var dc = this.dragCurrent;
1842
1843             // If the user did the mouse up outside of the window, we could
1844             // get here even though we have ended the drag.
1845             if (!dc || dc.isLocked()) {
1846                 return;
1847             }
1848
1849             var pt = e.getPoint();
1850
1851             // cache the previous dragOver array
1852             var oldOvers = [];
1853
1854             var outEvts   = [];
1855             var overEvts  = [];
1856             var dropEvts  = [];
1857             var enterEvts = [];
1858
1859             // Check to see if the object(s) we were hovering over is no longer
1860             // being hovered over so we can fire the onDragOut event
1861             for (var i in this.dragOvers) {
1862
1863                 var ddo = this.dragOvers[i];
1864
1865                 if (! this.isTypeOfDD(ddo)) {
1866                     continue;
1867                 }
1868
1869                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1870                     outEvts.push( ddo );
1871                 }
1872
1873                 oldOvers[i] = true;
1874                 delete this.dragOvers[i];
1875             }
1876
1877             for (var sGroup in dc.groups) {
1878
1879                 if ("string" != typeof sGroup) {
1880                     continue;
1881                 }
1882
1883                 for (i in this.ids[sGroup]) {
1884                     var oDD = this.ids[sGroup][i];
1885                     if (! this.isTypeOfDD(oDD)) {
1886                         continue;
1887                     }
1888
1889                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1890                         if (this.isOverTarget(pt, oDD, this.mode)) {
1891                             // look for drop interactions
1892                             if (isDrop) {
1893                                 dropEvts.push( oDD );
1894                             // look for drag enter and drag over interactions
1895                             } else {
1896
1897                                 // initial drag over: dragEnter fires
1898                                 if (!oldOvers[oDD.id]) {
1899                                     enterEvts.push( oDD );
1900                                 // subsequent drag overs: dragOver fires
1901                                 } else {
1902                                     overEvts.push( oDD );
1903                                 }
1904
1905                                 this.dragOvers[oDD.id] = oDD;
1906                             }
1907                         }
1908                     }
1909                 }
1910             }
1911
1912             if (this.mode) {
1913                 if (outEvts.length) {
1914                     dc.b4DragOut(e, outEvts);
1915                     dc.onDragOut(e, outEvts);
1916                 }
1917
1918                 if (enterEvts.length) {
1919                     dc.onDragEnter(e, enterEvts);
1920                 }
1921
1922                 if (overEvts.length) {
1923                     dc.b4DragOver(e, overEvts);
1924                     dc.onDragOver(e, overEvts);
1925                 }
1926
1927                 if (dropEvts.length) {
1928                     dc.b4DragDrop(e, dropEvts);
1929                     dc.onDragDrop(e, dropEvts);
1930                 }
1931
1932             } else {
1933                 // fire dragout events
1934                 var len = 0;
1935                 for (i=0, len=outEvts.length; i<len; ++i) {
1936                     dc.b4DragOut(e, outEvts[i].id);
1937                     dc.onDragOut(e, outEvts[i].id);
1938                 }
1939
1940                 // fire enter events
1941                 for (i=0,len=enterEvts.length; i<len; ++i) {
1942                     // dc.b4DragEnter(e, oDD.id);
1943                     dc.onDragEnter(e, enterEvts[i].id);
1944                 }
1945
1946                 // fire over events
1947                 for (i=0,len=overEvts.length; i<len; ++i) {
1948                     dc.b4DragOver(e, overEvts[i].id);
1949                     dc.onDragOver(e, overEvts[i].id);
1950                 }
1951
1952                 // fire drop events
1953                 for (i=0, len=dropEvts.length; i<len; ++i) {
1954                     dc.b4DragDrop(e, dropEvts[i].id);
1955                     dc.onDragDrop(e, dropEvts[i].id);
1956                 }
1957
1958             }
1959
1960             // notify about a drop that did not find a target
1961             if (isDrop && !dropEvts.length) {
1962                 dc.onInvalidDrop(e);
1963             }
1964
1965         },
1966
1967         /**
1968          * Helper function for getting the best match from the list of drag
1969          * and drop objects returned by the drag and drop events when we are
1970          * in INTERSECT mode.  It returns either the first object that the
1971          * cursor is over, or the object that has the greatest overlap with
1972          * the dragged element.
1973          * @method getBestMatch
1974          * @param  {DragDrop[]} dds The array of drag and drop objects
1975          * targeted
1976          * @return {DragDrop}       The best single match
1977          * @static
1978          */
1979         getBestMatch: function(dds) {
1980             var winner = null;
1981             // Return null if the input is not what we expect
1982             //if (!dds || !dds.length || dds.length == 0) {
1983                // winner = null;
1984             // If there is only one item, it wins
1985             //} else if (dds.length == 1) {
1986
1987             var len = dds.length;
1988
1989             if (len == 1) {
1990                 winner = dds[0];
1991             } else {
1992                 // Loop through the targeted items
1993                 for (var i=0; i<len; ++i) {
1994                     var dd = dds[i];
1995                     // If the cursor is over the object, it wins.  If the
1996                     // cursor is over multiple matches, the first one we come
1997                     // to wins.
1998                     if (dd.cursorIsOver) {
1999                         winner = dd;
2000                         break;
2001                     // Otherwise the object with the most overlap wins
2002                     } else {
2003                         if (!winner ||
2004                             winner.overlap.getArea() < dd.overlap.getArea()) {
2005                             winner = dd;
2006                         }
2007                     }
2008                 }
2009             }
2010
2011             return winner;
2012         },
2013
2014         /**
2015          * Refreshes the cache of the top-left and bottom-right points of the
2016          * drag and drop objects in the specified group(s).  This is in the
2017          * format that is stored in the drag and drop instance, so typical
2018          * usage is:
2019          * <code>
2020          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2021          * </code>
2022          * Alternatively:
2023          * <code>
2024          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2025          * </code>
2026          * @TODO this really should be an indexed array.  Alternatively this
2027          * method could accept both.
2028          * @method refreshCache
2029          * @param {Object} groups an associative array of groups to refresh
2030          * @static
2031          */
2032         refreshCache: function(groups) {
2033             for (var sGroup in groups) {
2034                 if ("string" != typeof sGroup) {
2035                     continue;
2036                 }
2037                 for (var i in this.ids[sGroup]) {
2038                     var oDD = this.ids[sGroup][i];
2039
2040                     if (this.isTypeOfDD(oDD)) {
2041                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2042                         var loc = this.getLocation(oDD);
2043                         if (loc) {
2044                             this.locationCache[oDD.id] = loc;
2045                         } else {
2046                             delete this.locationCache[oDD.id];
2047                             // this will unregister the drag and drop object if
2048                             // the element is not in a usable state
2049                             // oDD.unreg();
2050                         }
2051                     }
2052                 }
2053             }
2054         },
2055
2056         /**
2057          * This checks to make sure an element exists and is in the DOM.  The
2058          * main purpose is to handle cases where innerHTML is used to remove
2059          * drag and drop objects from the DOM.  IE provides an 'unspecified
2060          * error' when trying to access the offsetParent of such an element
2061          * @method verifyEl
2062          * @param {HTMLElement} el the element to check
2063          * @return {boolean} true if the element looks usable
2064          * @static
2065          */
2066         verifyEl: function(el) {
2067             if (el) {
2068                 var parent;
2069                 if(Roo.isIE){
2070                     try{
2071                         parent = el.offsetParent;
2072                     }catch(e){}
2073                 }else{
2074                     parent = el.offsetParent;
2075                 }
2076                 if (parent) {
2077                     return true;
2078                 }
2079             }
2080
2081             return false;
2082         },
2083
2084         /**
2085          * Returns a Region object containing the drag and drop element's position
2086          * and size, including the padding configured for it
2087          * @method getLocation
2088          * @param {DragDrop} oDD the drag and drop object to get the
2089          *                       location for
2090          * @return {Roo.lib.Region} a Region object representing the total area
2091          *                             the element occupies, including any padding
2092          *                             the instance is configured for.
2093          * @static
2094          */
2095         getLocation: function(oDD) {
2096             if (! this.isTypeOfDD(oDD)) {
2097                 return null;
2098             }
2099
2100             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2101
2102             try {
2103                 pos= Roo.lib.Dom.getXY(el);
2104             } catch (e) { }
2105
2106             if (!pos) {
2107                 return null;
2108             }
2109
2110             x1 = pos[0];
2111             x2 = x1 + el.offsetWidth;
2112             y1 = pos[1];
2113             y2 = y1 + el.offsetHeight;
2114
2115             t = y1 - oDD.padding[0];
2116             r = x2 + oDD.padding[1];
2117             b = y2 + oDD.padding[2];
2118             l = x1 - oDD.padding[3];
2119
2120             return new Roo.lib.Region( t, r, b, l );
2121         },
2122
2123         /**
2124          * Checks the cursor location to see if it over the target
2125          * @method isOverTarget
2126          * @param {Roo.lib.Point} pt The point to evaluate
2127          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2128          * @return {boolean} true if the mouse is over the target
2129          * @private
2130          * @static
2131          */
2132         isOverTarget: function(pt, oTarget, intersect) {
2133             // use cache if available
2134             var loc = this.locationCache[oTarget.id];
2135             if (!loc || !this.useCache) {
2136                 loc = this.getLocation(oTarget);
2137                 this.locationCache[oTarget.id] = loc;
2138
2139             }
2140
2141             if (!loc) {
2142                 return false;
2143             }
2144
2145             oTarget.cursorIsOver = loc.contains( pt );
2146
2147             // DragDrop is using this as a sanity check for the initial mousedown
2148             // in this case we are done.  In POINT mode, if the drag obj has no
2149             // contraints, we are also done. Otherwise we need to evaluate the
2150             // location of the target as related to the actual location of the
2151             // dragged element.
2152             var dc = this.dragCurrent;
2153             if (!dc || !dc.getTargetCoord ||
2154                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2155                 return oTarget.cursorIsOver;
2156             }
2157
2158             oTarget.overlap = null;
2159
2160             // Get the current location of the drag element, this is the
2161             // location of the mouse event less the delta that represents
2162             // where the original mousedown happened on the element.  We
2163             // need to consider constraints and ticks as well.
2164             var pos = dc.getTargetCoord(pt.x, pt.y);
2165
2166             var el = dc.getDragEl();
2167             var curRegion = new Roo.lib.Region( pos.y,
2168                                                    pos.x + el.offsetWidth,
2169                                                    pos.y + el.offsetHeight,
2170                                                    pos.x );
2171
2172             var overlap = curRegion.intersect(loc);
2173
2174             if (overlap) {
2175                 oTarget.overlap = overlap;
2176                 return (intersect) ? true : oTarget.cursorIsOver;
2177             } else {
2178                 return false;
2179             }
2180         },
2181
2182         /**
2183          * unload event handler
2184          * @method _onUnload
2185          * @private
2186          * @static
2187          */
2188         _onUnload: function(e, me) {
2189             Roo.dd.DragDropMgr.unregAll();
2190         },
2191
2192         /**
2193          * Cleans up the drag and drop events and objects.
2194          * @method unregAll
2195          * @private
2196          * @static
2197          */
2198         unregAll: function() {
2199
2200             if (this.dragCurrent) {
2201                 this.stopDrag();
2202                 this.dragCurrent = null;
2203             }
2204
2205             this._execOnAll("unreg", []);
2206
2207             for (i in this.elementCache) {
2208                 delete this.elementCache[i];
2209             }
2210
2211             this.elementCache = {};
2212             this.ids = {};
2213         },
2214
2215         /**
2216          * A cache of DOM elements
2217          * @property elementCache
2218          * @private
2219          * @static
2220          */
2221         elementCache: {},
2222
2223         /**
2224          * Get the wrapper for the DOM element specified
2225          * @method getElWrapper
2226          * @param {String} id the id of the element to get
2227          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2228          * @private
2229          * @deprecated This wrapper isn't that useful
2230          * @static
2231          */
2232         getElWrapper: function(id) {
2233             var oWrapper = this.elementCache[id];
2234             if (!oWrapper || !oWrapper.el) {
2235                 oWrapper = this.elementCache[id] =
2236                     new this.ElementWrapper(Roo.getDom(id));
2237             }
2238             return oWrapper;
2239         },
2240
2241         /**
2242          * Returns the actual DOM element
2243          * @method getElement
2244          * @param {String} id the id of the elment to get
2245          * @return {Object} The element
2246          * @deprecated use Roo.getDom instead
2247          * @static
2248          */
2249         getElement: function(id) {
2250             return Roo.getDom(id);
2251         },
2252
2253         /**
2254          * Returns the style property for the DOM element (i.e.,
2255          * document.getElById(id).style)
2256          * @method getCss
2257          * @param {String} id the id of the elment to get
2258          * @return {Object} The style property of the element
2259          * @deprecated use Roo.getDom instead
2260          * @static
2261          */
2262         getCss: function(id) {
2263             var el = Roo.getDom(id);
2264             return (el) ? el.style : null;
2265         },
2266
2267         /**
2268          * Inner class for cached elements
2269          * @class DragDropMgr.ElementWrapper
2270          * @for DragDropMgr
2271          * @private
2272          * @deprecated
2273          */
2274         ElementWrapper: function(el) {
2275                 /**
2276                  * The element
2277                  * @property el
2278                  */
2279                 this.el = el || null;
2280                 /**
2281                  * The element id
2282                  * @property id
2283                  */
2284                 this.id = this.el && el.id;
2285                 /**
2286                  * A reference to the style property
2287                  * @property css
2288                  */
2289                 this.css = this.el && el.style;
2290             },
2291
2292         /**
2293          * Returns the X position of an html element
2294          * @method getPosX
2295          * @param el the element for which to get the position
2296          * @return {int} the X coordinate
2297          * @for DragDropMgr
2298          * @deprecated use Roo.lib.Dom.getX instead
2299          * @static
2300          */
2301         getPosX: function(el) {
2302             return Roo.lib.Dom.getX(el);
2303         },
2304
2305         /**
2306          * Returns the Y position of an html element
2307          * @method getPosY
2308          * @param el the element for which to get the position
2309          * @return {int} the Y coordinate
2310          * @deprecated use Roo.lib.Dom.getY instead
2311          * @static
2312          */
2313         getPosY: function(el) {
2314             return Roo.lib.Dom.getY(el);
2315         },
2316
2317         /**
2318          * Swap two nodes.  In IE, we use the native method, for others we
2319          * emulate the IE behavior
2320          * @method swapNode
2321          * @param n1 the first node to swap
2322          * @param n2 the other node to swap
2323          * @static
2324          */
2325         swapNode: function(n1, n2) {
2326             if (n1.swapNode) {
2327                 n1.swapNode(n2);
2328             } else {
2329                 var p = n2.parentNode;
2330                 var s = n2.nextSibling;
2331
2332                 if (s == n1) {
2333                     p.insertBefore(n1, n2);
2334                 } else if (n2 == n1.nextSibling) {
2335                     p.insertBefore(n2, n1);
2336                 } else {
2337                     n1.parentNode.replaceChild(n2, n1);
2338                     p.insertBefore(n1, s);
2339                 }
2340             }
2341         },
2342
2343         /**
2344          * Returns the current scroll position
2345          * @method getScroll
2346          * @private
2347          * @static
2348          */
2349         getScroll: function () {
2350             var t, l, dde=document.documentElement, db=document.body;
2351             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2352                 t = dde.scrollTop;
2353                 l = dde.scrollLeft;
2354             } else if (db) {
2355                 t = db.scrollTop;
2356                 l = db.scrollLeft;
2357             } else {
2358
2359             }
2360             return { top: t, left: l };
2361         },
2362
2363         /**
2364          * Returns the specified element style property
2365          * @method getStyle
2366          * @param {HTMLElement} el          the element
2367          * @param {string}      styleProp   the style property
2368          * @return {string} The value of the style property
2369          * @deprecated use Roo.lib.Dom.getStyle
2370          * @static
2371          */
2372         getStyle: function(el, styleProp) {
2373             return Roo.fly(el).getStyle(styleProp);
2374         },
2375
2376         /**
2377          * Gets the scrollTop
2378          * @method getScrollTop
2379          * @return {int} the document's scrollTop
2380          * @static
2381          */
2382         getScrollTop: function () { return this.getScroll().top; },
2383
2384         /**
2385          * Gets the scrollLeft
2386          * @method getScrollLeft
2387          * @return {int} the document's scrollTop
2388          * @static
2389          */
2390         getScrollLeft: function () { return this.getScroll().left; },
2391
2392         /**
2393          * Sets the x/y position of an element to the location of the
2394          * target element.
2395          * @method moveToEl
2396          * @param {HTMLElement} moveEl      The element to move
2397          * @param {HTMLElement} targetEl    The position reference element
2398          * @static
2399          */
2400         moveToEl: function (moveEl, targetEl) {
2401             var aCoord = Roo.lib.Dom.getXY(targetEl);
2402             Roo.lib.Dom.setXY(moveEl, aCoord);
2403         },
2404
2405         /**
2406          * Numeric array sort function
2407          * @method numericSort
2408          * @static
2409          */
2410         numericSort: function(a, b) { return (a - b); },
2411
2412         /**
2413          * Internal counter
2414          * @property _timeoutCount
2415          * @private
2416          * @static
2417          */
2418         _timeoutCount: 0,
2419
2420         /**
2421          * Trying to make the load order less important.  Without this we get
2422          * an error if this file is loaded before the Event Utility.
2423          * @method _addListeners
2424          * @private
2425          * @static
2426          */
2427         _addListeners: function() {
2428             var DDM = Roo.dd.DDM;
2429             if ( Roo.lib.Event && document ) {
2430                 DDM._onLoad();
2431             } else {
2432                 if (DDM._timeoutCount > 2000) {
2433                 } else {
2434                     setTimeout(DDM._addListeners, 10);
2435                     if (document && document.body) {
2436                         DDM._timeoutCount += 1;
2437                     }
2438                 }
2439             }
2440         },
2441
2442         /**
2443          * Recursively searches the immediate parent and all child nodes for
2444          * the handle element in order to determine wheter or not it was
2445          * clicked.
2446          * @method handleWasClicked
2447          * @param node the html element to inspect
2448          * @static
2449          */
2450         handleWasClicked: function(node, id) {
2451             if (this.isHandle(id, node.id)) {
2452                 return true;
2453             } else {
2454                 // check to see if this is a text node child of the one we want
2455                 var p = node.parentNode;
2456
2457                 while (p) {
2458                     if (this.isHandle(id, p.id)) {
2459                         return true;
2460                     } else {
2461                         p = p.parentNode;
2462                     }
2463                 }
2464             }
2465
2466             return false;
2467         }
2468
2469     };
2470
2471 }();
2472
2473 // shorter alias, save a few bytes
2474 Roo.dd.DDM = Roo.dd.DragDropMgr;
2475 Roo.dd.DDM._addListeners();
2476
2477 }/*
2478  * Based on:
2479  * Ext JS Library 1.1.1
2480  * Copyright(c) 2006-2007, Ext JS, LLC.
2481  *
2482  * Originally Released Under LGPL - original licence link has changed is not relivant.
2483  *
2484  * Fork - LGPL
2485  * <script type="text/javascript">
2486  */
2487
2488 /**
2489  * @class Roo.dd.DD
2490  * A DragDrop implementation where the linked element follows the
2491  * mouse cursor during a drag.
2492  * @extends Roo.dd.DragDrop
2493  * @constructor
2494  * @param {String} id the id of the linked element
2495  * @param {String} sGroup the group of related DragDrop items
2496  * @param {object} config an object containing configurable attributes
2497  *                Valid properties for DD:
2498  *                    scroll
2499  */
2500 Roo.dd.DD = function(id, sGroup, config) {
2501     if (id) {
2502         this.init(id, sGroup, config);
2503     }
2504 };
2505
2506 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2507
2508     /**
2509      * When set to true, the utility automatically tries to scroll the browser
2510      * window wehn a drag and drop element is dragged near the viewport boundary.
2511      * Defaults to true.
2512      * @property scroll
2513      * @type boolean
2514      */
2515     scroll: true,
2516
2517     /**
2518      * Sets the pointer offset to the distance between the linked element's top
2519      * left corner and the location the element was clicked
2520      * @method autoOffset
2521      * @param {int} iPageX the X coordinate of the click
2522      * @param {int} iPageY the Y coordinate of the click
2523      */
2524     autoOffset: function(iPageX, iPageY) {
2525         var x = iPageX - this.startPageX;
2526         var y = iPageY - this.startPageY;
2527         this.setDelta(x, y);
2528     },
2529
2530     /**
2531      * Sets the pointer offset.  You can call this directly to force the
2532      * offset to be in a particular location (e.g., pass in 0,0 to set it
2533      * to the center of the object)
2534      * @method setDelta
2535      * @param {int} iDeltaX the distance from the left
2536      * @param {int} iDeltaY the distance from the top
2537      */
2538     setDelta: function(iDeltaX, iDeltaY) {
2539         this.deltaX = iDeltaX;
2540         this.deltaY = iDeltaY;
2541     },
2542
2543     /**
2544      * Sets the drag element to the location of the mousedown or click event,
2545      * maintaining the cursor location relative to the location on the element
2546      * that was clicked.  Override this if you want to place the element in a
2547      * location other than where the cursor is.
2548      * @method setDragElPos
2549      * @param {int} iPageX the X coordinate of the mousedown or drag event
2550      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2551      */
2552     setDragElPos: function(iPageX, iPageY) {
2553         // the first time we do this, we are going to check to make sure
2554         // the element has css positioning
2555
2556         var el = this.getDragEl();
2557         this.alignElWithMouse(el, iPageX, iPageY);
2558     },
2559
2560     /**
2561      * Sets the element to the location of the mousedown or click event,
2562      * maintaining the cursor location relative to the location on the element
2563      * that was clicked.  Override this if you want to place the element in a
2564      * location other than where the cursor is.
2565      * @method alignElWithMouse
2566      * @param {HTMLElement} el the element to move
2567      * @param {int} iPageX the X coordinate of the mousedown or drag event
2568      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2569      */
2570     alignElWithMouse: function(el, iPageX, iPageY) {
2571         var oCoord = this.getTargetCoord(iPageX, iPageY);
2572         var fly = el.dom ? el : Roo.fly(el);
2573         if (!this.deltaSetXY) {
2574             var aCoord = [oCoord.x, oCoord.y];
2575             fly.setXY(aCoord);
2576             var newLeft = fly.getLeft(true);
2577             var newTop  = fly.getTop(true);
2578             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2579         } else {
2580             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2581         }
2582
2583         this.cachePosition(oCoord.x, oCoord.y);
2584         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2585         return oCoord;
2586     },
2587
2588     /**
2589      * Saves the most recent position so that we can reset the constraints and
2590      * tick marks on-demand.  We need to know this so that we can calculate the
2591      * number of pixels the element is offset from its original position.
2592      * @method cachePosition
2593      * @param iPageX the current x position (optional, this just makes it so we
2594      * don't have to look it up again)
2595      * @param iPageY the current y position (optional, this just makes it so we
2596      * don't have to look it up again)
2597      */
2598     cachePosition: function(iPageX, iPageY) {
2599         if (iPageX) {
2600             this.lastPageX = iPageX;
2601             this.lastPageY = iPageY;
2602         } else {
2603             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2604             this.lastPageX = aCoord[0];
2605             this.lastPageY = aCoord[1];
2606         }
2607     },
2608
2609     /**
2610      * Auto-scroll the window if the dragged object has been moved beyond the
2611      * visible window boundary.
2612      * @method autoScroll
2613      * @param {int} x the drag element's x position
2614      * @param {int} y the drag element's y position
2615      * @param {int} h the height of the drag element
2616      * @param {int} w the width of the drag element
2617      * @private
2618      */
2619     autoScroll: function(x, y, h, w) {
2620
2621         if (this.scroll) {
2622             // The client height
2623             var clientH = Roo.lib.Dom.getViewWidth();
2624
2625             // The client width
2626             var clientW = Roo.lib.Dom.getViewHeight();
2627
2628             // The amt scrolled down
2629             var st = this.DDM.getScrollTop();
2630
2631             // The amt scrolled right
2632             var sl = this.DDM.getScrollLeft();
2633
2634             // Location of the bottom of the element
2635             var bot = h + y;
2636
2637             // Location of the right of the element
2638             var right = w + x;
2639
2640             // The distance from the cursor to the bottom of the visible area,
2641             // adjusted so that we don't scroll if the cursor is beyond the
2642             // element drag constraints
2643             var toBot = (clientH + st - y - this.deltaY);
2644
2645             // The distance from the cursor to the right of the visible area
2646             var toRight = (clientW + sl - x - this.deltaX);
2647
2648
2649             // How close to the edge the cursor must be before we scroll
2650             // var thresh = (document.all) ? 100 : 40;
2651             var thresh = 40;
2652
2653             // How many pixels to scroll per autoscroll op.  This helps to reduce
2654             // clunky scrolling. IE is more sensitive about this ... it needs this
2655             // value to be higher.
2656             var scrAmt = (document.all) ? 80 : 30;
2657
2658             // Scroll down if we are near the bottom of the visible page and the
2659             // obj extends below the crease
2660             if ( bot > clientH && toBot < thresh ) {
2661                 window.scrollTo(sl, st + scrAmt);
2662             }
2663
2664             // Scroll up if the window is scrolled down and the top of the object
2665             // goes above the top border
2666             if ( y < st && st > 0 && y - st < thresh ) {
2667                 window.scrollTo(sl, st - scrAmt);
2668             }
2669
2670             // Scroll right if the obj is beyond the right border and the cursor is
2671             // near the border.
2672             if ( right > clientW && toRight < thresh ) {
2673                 window.scrollTo(sl + scrAmt, st);
2674             }
2675
2676             // Scroll left if the window has been scrolled to the right and the obj
2677             // extends past the left border
2678             if ( x < sl && sl > 0 && x - sl < thresh ) {
2679                 window.scrollTo(sl - scrAmt, st);
2680             }
2681         }
2682     },
2683
2684     /**
2685      * Finds the location the element should be placed if we want to move
2686      * it to where the mouse location less the click offset would place us.
2687      * @method getTargetCoord
2688      * @param {int} iPageX the X coordinate of the click
2689      * @param {int} iPageY the Y coordinate of the click
2690      * @return an object that contains the coordinates (Object.x and Object.y)
2691      * @private
2692      */
2693     getTargetCoord: function(iPageX, iPageY) {
2694
2695
2696         var x = iPageX - this.deltaX;
2697         var y = iPageY - this.deltaY;
2698
2699         if (this.constrainX) {
2700             if (x < this.minX) { x = this.minX; }
2701             if (x > this.maxX) { x = this.maxX; }
2702         }
2703
2704         if (this.constrainY) {
2705             if (y < this.minY) { y = this.minY; }
2706             if (y > this.maxY) { y = this.maxY; }
2707         }
2708
2709         x = this.getTick(x, this.xTicks);
2710         y = this.getTick(y, this.yTicks);
2711
2712
2713         return {x:x, y:y};
2714     },
2715
2716     /*
2717      * Sets up config options specific to this class. Overrides
2718      * Roo.dd.DragDrop, but all versions of this method through the
2719      * inheritance chain are called
2720      */
2721     applyConfig: function() {
2722         Roo.dd.DD.superclass.applyConfig.call(this);
2723         this.scroll = (this.config.scroll !== false);
2724     },
2725
2726     /*
2727      * Event that fires prior to the onMouseDown event.  Overrides
2728      * Roo.dd.DragDrop.
2729      */
2730     b4MouseDown: function(e) {
2731         // this.resetConstraints();
2732         this.autoOffset(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     /*
2737      * Event that fires prior to the onDrag event.  Overrides
2738      * Roo.dd.DragDrop.
2739      */
2740     b4Drag: function(e) {
2741         this.setDragElPos(e.getPageX(),
2742                             e.getPageY());
2743     },
2744
2745     toString: function() {
2746         return ("DD " + this.id);
2747     }
2748
2749     //////////////////////////////////////////////////////////////////////////
2750     // Debugging ygDragDrop events that can be overridden
2751     //////////////////////////////////////////////////////////////////////////
2752     /*
2753     startDrag: function(x, y) {
2754     },
2755
2756     onDrag: function(e) {
2757     },
2758
2759     onDragEnter: function(e, id) {
2760     },
2761
2762     onDragOver: function(e, id) {
2763     },
2764
2765     onDragOut: function(e, id) {
2766     },
2767
2768     onDragDrop: function(e, id) {
2769     },
2770
2771     endDrag: function(e) {
2772     }
2773
2774     */
2775
2776 });/*
2777  * Based on:
2778  * Ext JS Library 1.1.1
2779  * Copyright(c) 2006-2007, Ext JS, LLC.
2780  *
2781  * Originally Released Under LGPL - original licence link has changed is not relivant.
2782  *
2783  * Fork - LGPL
2784  * <script type="text/javascript">
2785  */
2786
2787 /**
2788  * @class Roo.dd.DDProxy
2789  * A DragDrop implementation that inserts an empty, bordered div into
2790  * the document that follows the cursor during drag operations.  At the time of
2791  * the click, the frame div is resized to the dimensions of the linked html
2792  * element, and moved to the exact location of the linked element.
2793  *
2794  * References to the "frame" element refer to the single proxy element that
2795  * was created to be dragged in place of all DDProxy elements on the
2796  * page.
2797  *
2798  * @extends Roo.dd.DD
2799  * @constructor
2800  * @param {String} id the id of the linked html element
2801  * @param {String} sGroup the group of related DragDrop objects
2802  * @param {object} config an object containing configurable attributes
2803  *                Valid properties for DDProxy in addition to those in DragDrop:
2804  *                   resizeFrame, centerFrame, dragElId
2805  */
2806 Roo.dd.DDProxy = function(id, sGroup, config) {
2807     if (id) {
2808         this.init(id, sGroup, config);
2809         this.initFrame();
2810     }
2811 };
2812
2813 /**
2814  * The default drag frame div id
2815  * @property Roo.dd.DDProxy.dragElId
2816  * @type String
2817  * @static
2818  */
2819 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2820
2821 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2822
2823     /**
2824      * By default we resize the drag frame to be the same size as the element
2825      * we want to drag (this is to get the frame effect).  We can turn it off
2826      * if we want a different behavior.
2827      * @property resizeFrame
2828      * @type boolean
2829      */
2830     resizeFrame: true,
2831
2832     /**
2833      * By default the frame is positioned exactly where the drag element is, so
2834      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2835      * you do not have constraints on the obj is to have the drag frame centered
2836      * around the cursor.  Set centerFrame to true for this effect.
2837      * @property centerFrame
2838      * @type boolean
2839      */
2840     centerFrame: false,
2841
2842     /**
2843      * Creates the proxy element if it does not yet exist
2844      * @method createFrame
2845      */
2846     createFrame: function() {
2847         var self = this;
2848         var body = document.body;
2849
2850         if (!body || !body.firstChild) {
2851             setTimeout( function() { self.createFrame(); }, 50 );
2852             return;
2853         }
2854
2855         var div = this.getDragEl();
2856
2857         if (!div) {
2858             div    = document.createElement("div");
2859             div.id = this.dragElId;
2860             var s  = div.style;
2861
2862             s.position   = "absolute";
2863             s.visibility = "hidden";
2864             s.cursor     = "move";
2865             s.border     = "2px solid #aaa";
2866             s.zIndex     = 999;
2867
2868             // appendChild can blow up IE if invoked prior to the window load event
2869             // while rendering a table.  It is possible there are other scenarios
2870             // that would cause this to happen as well.
2871             body.insertBefore(div, body.firstChild);
2872         }
2873     },
2874
2875     /**
2876      * Initialization for the drag frame element.  Must be called in the
2877      * constructor of all subclasses
2878      * @method initFrame
2879      */
2880     initFrame: function() {
2881         this.createFrame();
2882     },
2883
2884     applyConfig: function() {
2885         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2886
2887         this.resizeFrame = (this.config.resizeFrame !== false);
2888         this.centerFrame = (this.config.centerFrame);
2889         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2890     },
2891
2892     /**
2893      * Resizes the drag frame to the dimensions of the clicked object, positions
2894      * it over the object, and finally displays it
2895      * @method showFrame
2896      * @param {int} iPageX X click position
2897      * @param {int} iPageY Y click position
2898      * @private
2899      */
2900     showFrame: function(iPageX, iPageY) {
2901         var el = this.getEl();
2902         var dragEl = this.getDragEl();
2903         var s = dragEl.style;
2904
2905         this._resizeProxy();
2906
2907         if (this.centerFrame) {
2908             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2909                            Math.round(parseInt(s.height, 10)/2) );
2910         }
2911
2912         this.setDragElPos(iPageX, iPageY);
2913
2914         Roo.fly(dragEl).show();
2915     },
2916
2917     /**
2918      * The proxy is automatically resized to the dimensions of the linked
2919      * element when a drag is initiated, unless resizeFrame is set to false
2920      * @method _resizeProxy
2921      * @private
2922      */
2923     _resizeProxy: function() {
2924         if (this.resizeFrame) {
2925             var el = this.getEl();
2926             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2927         }
2928     },
2929
2930     // overrides Roo.dd.DragDrop
2931     b4MouseDown: function(e) {
2932         var x = e.getPageX();
2933         var y = e.getPageY();
2934         this.autoOffset(x, y);
2935         this.setDragElPos(x, y);
2936     },
2937
2938     // overrides Roo.dd.DragDrop
2939     b4StartDrag: function(x, y) {
2940         // show the drag frame
2941         this.showFrame(x, y);
2942     },
2943
2944     // overrides Roo.dd.DragDrop
2945     b4EndDrag: function(e) {
2946         Roo.fly(this.getDragEl()).hide();
2947     },
2948
2949     // overrides Roo.dd.DragDrop
2950     // By default we try to move the element to the last location of the frame.
2951     // This is so that the default behavior mirrors that of Roo.dd.DD.
2952     endDrag: function(e) {
2953
2954         var lel = this.getEl();
2955         var del = this.getDragEl();
2956
2957         // Show the drag frame briefly so we can get its position
2958         del.style.visibility = "";
2959
2960         this.beforeMove();
2961         // Hide the linked element before the move to get around a Safari
2962         // rendering bug.
2963         lel.style.visibility = "hidden";
2964         Roo.dd.DDM.moveToEl(lel, del);
2965         del.style.visibility = "hidden";
2966         lel.style.visibility = "";
2967
2968         this.afterDrag();
2969     },
2970
2971     beforeMove : function(){
2972
2973     },
2974
2975     afterDrag : function(){
2976
2977     },
2978
2979     toString: function() {
2980         return ("DDProxy " + this.id);
2981     }
2982
2983 });
2984 /*
2985  * Based on:
2986  * Ext JS Library 1.1.1
2987  * Copyright(c) 2006-2007, Ext JS, LLC.
2988  *
2989  * Originally Released Under LGPL - original licence link has changed is not relivant.
2990  *
2991  * Fork - LGPL
2992  * <script type="text/javascript">
2993  */
2994
2995  /**
2996  * @class Roo.dd.DDTarget
2997  * A DragDrop implementation that does not move, but can be a drop
2998  * target.  You would get the same result by simply omitting implementation
2999  * for the event callbacks, but this way we reduce the processing cost of the
3000  * event listener and the callbacks.
3001  * @extends Roo.dd.DragDrop
3002  * @constructor
3003  * @param {String} id the id of the element that is a drop target
3004  * @param {String} sGroup the group of related DragDrop objects
3005  * @param {object} config an object containing configurable attributes
3006  *                 Valid properties for DDTarget in addition to those in
3007  *                 DragDrop:
3008  *                    none
3009  */
3010 Roo.dd.DDTarget = function(id, sGroup, config) {
3011     if (id) {
3012         this.initTarget(id, sGroup, config);
3013     }
3014     if (config.listeners || config.events) { 
3015        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3016             listeners : config.listeners || {}, 
3017             events : config.events || {} 
3018         });    
3019     }
3020 };
3021
3022 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3023 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3024     toString: function() {
3025         return ("DDTarget " + this.id);
3026     }
3027 });
3028 /*
3029  * Based on:
3030  * Ext JS Library 1.1.1
3031  * Copyright(c) 2006-2007, Ext JS, LLC.
3032  *
3033  * Originally Released Under LGPL - original licence link has changed is not relivant.
3034  *
3035  * Fork - LGPL
3036  * <script type="text/javascript">
3037  */
3038  
3039
3040 /**
3041  * @class Roo.dd.ScrollManager
3042  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3043  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3044  * @singleton
3045  */
3046 Roo.dd.ScrollManager = function(){
3047     var ddm = Roo.dd.DragDropMgr;
3048     var els = {};
3049     var dragEl = null;
3050     var proc = {};
3051     
3052     
3053     
3054     var onStop = function(e){
3055         dragEl = null;
3056         clearProc();
3057     };
3058     
3059     var triggerRefresh = function(){
3060         if(ddm.dragCurrent){
3061              ddm.refreshCache(ddm.dragCurrent.groups);
3062         }
3063     };
3064     
3065     var doScroll = function(){
3066         if(ddm.dragCurrent){
3067             var dds = Roo.dd.ScrollManager;
3068             if(!dds.animate){
3069                 if(proc.el.scroll(proc.dir, dds.increment)){
3070                     triggerRefresh();
3071                 }
3072             }else{
3073                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3074             }
3075         }
3076     };
3077     
3078     var clearProc = function(){
3079         if(proc.id){
3080             clearInterval(proc.id);
3081         }
3082         proc.id = 0;
3083         proc.el = null;
3084         proc.dir = "";
3085     };
3086     
3087     var startProc = function(el, dir){
3088          Roo.log('scroll startproc');
3089         clearProc();
3090         proc.el = el;
3091         proc.dir = dir;
3092         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3093     };
3094     
3095     var onFire = function(e, isDrop){
3096        
3097         if(isDrop || !ddm.dragCurrent){ return; }
3098         var dds = Roo.dd.ScrollManager;
3099         if(!dragEl || dragEl != ddm.dragCurrent){
3100             dragEl = ddm.dragCurrent;
3101             // refresh regions on drag start
3102             dds.refreshCache();
3103         }
3104         
3105         var xy = Roo.lib.Event.getXY(e);
3106         var pt = new Roo.lib.Point(xy[0], xy[1]);
3107         for(var id in els){
3108             var el = els[id], r = el._region;
3109             if(r && r.contains(pt) && el.isScrollable()){
3110                 if(r.bottom - pt.y <= dds.thresh){
3111                     if(proc.el != el){
3112                         startProc(el, "down");
3113                     }
3114                     return;
3115                 }else if(r.right - pt.x <= dds.thresh){
3116                     if(proc.el != el){
3117                         startProc(el, "left");
3118                     }
3119                     return;
3120                 }else if(pt.y - r.top <= dds.thresh){
3121                     if(proc.el != el){
3122                         startProc(el, "up");
3123                     }
3124                     return;
3125                 }else if(pt.x - r.left <= dds.thresh){
3126                     if(proc.el != el){
3127                         startProc(el, "right");
3128                     }
3129                     return;
3130                 }
3131             }
3132         }
3133         clearProc();
3134     };
3135     
3136     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3137     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3138     
3139     return {
3140         /**
3141          * Registers new overflow element(s) to auto scroll
3142          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3143          */
3144         register : function(el){
3145             if(el instanceof Array){
3146                 for(var i = 0, len = el.length; i < len; i++) {
3147                         this.register(el[i]);
3148                 }
3149             }else{
3150                 el = Roo.get(el);
3151                 els[el.id] = el;
3152             }
3153             Roo.dd.ScrollManager.els = els;
3154         },
3155         
3156         /**
3157          * Unregisters overflow element(s) so they are no longer scrolled
3158          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3159          */
3160         unregister : function(el){
3161             if(el instanceof Array){
3162                 for(var i = 0, len = el.length; i < len; i++) {
3163                         this.unregister(el[i]);
3164                 }
3165             }else{
3166                 el = Roo.get(el);
3167                 delete els[el.id];
3168             }
3169         },
3170         
3171         /**
3172          * The number of pixels from the edge of a container the pointer needs to be to 
3173          * trigger scrolling (defaults to 25)
3174          * @type Number
3175          */
3176         thresh : 25,
3177         
3178         /**
3179          * The number of pixels to scroll in each scroll increment (defaults to 50)
3180          * @type Number
3181          */
3182         increment : 100,
3183         
3184         /**
3185          * The frequency of scrolls in milliseconds (defaults to 500)
3186          * @type Number
3187          */
3188         frequency : 500,
3189         
3190         /**
3191          * True to animate the scroll (defaults to true)
3192          * @type Boolean
3193          */
3194         animate: true,
3195         
3196         /**
3197          * The animation duration in seconds - 
3198          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3199          * @type Number
3200          */
3201         animDuration: .4,
3202         
3203         /**
3204          * Manually trigger a cache refresh.
3205          */
3206         refreshCache : function(){
3207             for(var id in els){
3208                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3209                     els[id]._region = els[id].getRegion();
3210                 }
3211             }
3212         }
3213     };
3214 }();/*
3215  * Based on:
3216  * Ext JS Library 1.1.1
3217  * Copyright(c) 2006-2007, Ext JS, LLC.
3218  *
3219  * Originally Released Under LGPL - original licence link has changed is not relivant.
3220  *
3221  * Fork - LGPL
3222  * <script type="text/javascript">
3223  */
3224  
3225
3226 /**
3227  * @class Roo.dd.Registry
3228  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3229  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3230  * @singleton
3231  */
3232 Roo.dd.Registry = function(){
3233     var elements = {}; 
3234     var handles = {}; 
3235     var autoIdSeed = 0;
3236
3237     var getId = function(el, autogen){
3238         if(typeof el == "string"){
3239             return el;
3240         }
3241         var id = el.id;
3242         if(!id && autogen !== false){
3243             id = "roodd-" + (++autoIdSeed);
3244             el.id = id;
3245         }
3246         return id;
3247     };
3248     
3249     return {
3250     /**
3251      * Register a drag drop element
3252      * @param {String|HTMLElement} element The id or DOM node to register
3253      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3254      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3255      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3256      * populated in the data object (if applicable):
3257      * <pre>
3258 Value      Description<br />
3259 ---------  ------------------------------------------<br />
3260 handles    Array of DOM nodes that trigger dragging<br />
3261            for the element being registered<br />
3262 isHandle   True if the element passed in triggers<br />
3263            dragging itself, else false
3264 </pre>
3265      */
3266         register : function(el, data){
3267             data = data || {};
3268             if(typeof el == "string"){
3269                 el = document.getElementById(el);
3270             }
3271             data.ddel = el;
3272             elements[getId(el)] = data;
3273             if(data.isHandle !== false){
3274                 handles[data.ddel.id] = data;
3275             }
3276             if(data.handles){
3277                 var hs = data.handles;
3278                 for(var i = 0, len = hs.length; i < len; i++){
3279                         handles[getId(hs[i])] = data;
3280                 }
3281             }
3282         },
3283
3284     /**
3285      * Unregister a drag drop element
3286      * @param {String|HTMLElement}  element The id or DOM node to unregister
3287      */
3288         unregister : function(el){
3289             var id = getId(el, false);
3290             var data = elements[id];
3291             if(data){
3292                 delete elements[id];
3293                 if(data.handles){
3294                     var hs = data.handles;
3295                     for(var i = 0, len = hs.length; i < len; i++){
3296                         delete handles[getId(hs[i], false)];
3297                     }
3298                 }
3299             }
3300         },
3301
3302     /**
3303      * Returns the handle registered for a DOM Node by id
3304      * @param {String|HTMLElement} id The DOM node or id to look up
3305      * @return {Object} handle The custom handle data
3306      */
3307         getHandle : function(id){
3308             if(typeof id != "string"){ // must be element?
3309                 id = id.id;
3310             }
3311             return handles[id];
3312         },
3313
3314     /**
3315      * Returns the handle that is registered for the DOM node that is the target of the event
3316      * @param {Event} e The event
3317      * @return {Object} handle The custom handle data
3318      */
3319         getHandleFromEvent : function(e){
3320             var t = Roo.lib.Event.getTarget(e);
3321             return t ? handles[t.id] : null;
3322         },
3323
3324     /**
3325      * Returns a custom data object that is registered for a DOM node by id
3326      * @param {String|HTMLElement} id The DOM node or id to look up
3327      * @return {Object} data The custom data
3328      */
3329         getTarget : function(id){
3330             if(typeof id != "string"){ // must be element?
3331                 id = id.id;
3332             }
3333             return elements[id];
3334         },
3335
3336     /**
3337      * Returns a custom data object that is registered for the DOM node that is the target of the event
3338      * @param {Event} e The event
3339      * @return {Object} data The custom data
3340      */
3341         getTargetFromEvent : function(e){
3342             var t = Roo.lib.Event.getTarget(e);
3343             return t ? elements[t.id] || handles[t.id] : null;
3344         }
3345     };
3346 }();/*
3347  * Based on:
3348  * Ext JS Library 1.1.1
3349  * Copyright(c) 2006-2007, Ext JS, LLC.
3350  *
3351  * Originally Released Under LGPL - original licence link has changed is not relivant.
3352  *
3353  * Fork - LGPL
3354  * <script type="text/javascript">
3355  */
3356  
3357
3358 /**
3359  * @class Roo.dd.StatusProxy
3360  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3361  * default drag proxy used by all Roo.dd components.
3362  * @constructor
3363  * @param {Object} config
3364  */
3365 Roo.dd.StatusProxy = function(config){
3366     Roo.apply(this, config);
3367     this.id = this.id || Roo.id();
3368     this.el = new Roo.Layer({
3369         dh: {
3370             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3371                 {tag: "div", cls: "x-dd-drop-icon"},
3372                 {tag: "div", cls: "x-dd-drag-ghost"}
3373             ]
3374         }, 
3375         shadow: !config || config.shadow !== false
3376     });
3377     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3378     this.dropStatus = this.dropNotAllowed;
3379 };
3380
3381 Roo.dd.StatusProxy.prototype = {
3382     /**
3383      * @cfg {String} dropAllowed
3384      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3385      */
3386     dropAllowed : "x-dd-drop-ok",
3387     /**
3388      * @cfg {String} dropNotAllowed
3389      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3390      */
3391     dropNotAllowed : "x-dd-drop-nodrop",
3392
3393     /**
3394      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3395      * over the current target element.
3396      * @param {String} cssClass The css class for the new drop status indicator image
3397      */
3398     setStatus : function(cssClass){
3399         cssClass = cssClass || this.dropNotAllowed;
3400         if(this.dropStatus != cssClass){
3401             this.el.replaceClass(this.dropStatus, cssClass);
3402             this.dropStatus = cssClass;
3403         }
3404     },
3405
3406     /**
3407      * Resets the status indicator to the default dropNotAllowed value
3408      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3409      */
3410     reset : function(clearGhost){
3411         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3412         this.dropStatus = this.dropNotAllowed;
3413         if(clearGhost){
3414             this.ghost.update("");
3415         }
3416     },
3417
3418     /**
3419      * Updates the contents of the ghost element
3420      * @param {String} html The html that will replace the current innerHTML of the ghost element
3421      */
3422     update : function(html){
3423         if(typeof html == "string"){
3424             this.ghost.update(html);
3425         }else{
3426             this.ghost.update("");
3427             html.style.margin = "0";
3428             this.ghost.dom.appendChild(html);
3429         }
3430         // ensure float = none set?? cant remember why though.
3431         var el = this.ghost.dom.firstChild;
3432                 if(el){
3433                         Roo.fly(el).setStyle('float', 'none');
3434                 }
3435     },
3436     
3437     /**
3438      * Returns the underlying proxy {@link Roo.Layer}
3439      * @return {Roo.Layer} el
3440     */
3441     getEl : function(){
3442         return this.el;
3443     },
3444
3445     /**
3446      * Returns the ghost element
3447      * @return {Roo.Element} el
3448      */
3449     getGhost : function(){
3450         return this.ghost;
3451     },
3452
3453     /**
3454      * Hides the proxy
3455      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3456      */
3457     hide : function(clear){
3458         this.el.hide();
3459         if(clear){
3460             this.reset(true);
3461         }
3462     },
3463
3464     /**
3465      * Stops the repair animation if it's currently running
3466      */
3467     stop : function(){
3468         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3469             this.anim.stop();
3470         }
3471     },
3472
3473     /**
3474      * Displays this proxy
3475      */
3476     show : function(){
3477         this.el.show();
3478     },
3479
3480     /**
3481      * Force the Layer to sync its shadow and shim positions to the element
3482      */
3483     sync : function(){
3484         this.el.sync();
3485     },
3486
3487     /**
3488      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3489      * invalid drop operation by the item being dragged.
3490      * @param {Array} xy The XY position of the element ([x, y])
3491      * @param {Function} callback The function to call after the repair is complete
3492      * @param {Object} scope The scope in which to execute the callback
3493      */
3494     repair : function(xy, callback, scope){
3495         this.callback = callback;
3496         this.scope = scope;
3497         if(xy && this.animRepair !== false){
3498             this.el.addClass("x-dd-drag-repair");
3499             this.el.hideUnders(true);
3500             this.anim = this.el.shift({
3501                 duration: this.repairDuration || .5,
3502                 easing: 'easeOut',
3503                 xy: xy,
3504                 stopFx: true,
3505                 callback: this.afterRepair,
3506                 scope: this
3507             });
3508         }else{
3509             this.afterRepair();
3510         }
3511     },
3512
3513     // private
3514     afterRepair : function(){
3515         this.hide(true);
3516         if(typeof this.callback == "function"){
3517             this.callback.call(this.scope || this);
3518         }
3519         this.callback = null;
3520         this.scope = null;
3521     }
3522 };/*
3523  * Based on:
3524  * Ext JS Library 1.1.1
3525  * Copyright(c) 2006-2007, Ext JS, LLC.
3526  *
3527  * Originally Released Under LGPL - original licence link has changed is not relivant.
3528  *
3529  * Fork - LGPL
3530  * <script type="text/javascript">
3531  */
3532
3533 /**
3534  * @class Roo.dd.DragSource
3535  * @extends Roo.dd.DDProxy
3536  * A simple class that provides the basic implementation needed to make any element draggable.
3537  * @constructor
3538  * @param {String/HTMLElement/Element} el The container element
3539  * @param {Object} config
3540  */
3541 Roo.dd.DragSource = function(el, config){
3542     this.el = Roo.get(el);
3543     this.dragData = {};
3544     
3545     Roo.apply(this, config);
3546     
3547     if(!this.proxy){
3548         this.proxy = new Roo.dd.StatusProxy();
3549     }
3550
3551     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3552           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3553     
3554     this.dragging = false;
3555 };
3556
3557 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3558     /**
3559      * @cfg {String} dropAllowed
3560      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3561      */
3562     dropAllowed : "x-dd-drop-ok",
3563     /**
3564      * @cfg {String} dropNotAllowed
3565      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3566      */
3567     dropNotAllowed : "x-dd-drop-nodrop",
3568
3569     /**
3570      * Returns the data object associated with this drag source
3571      * @return {Object} data An object containing arbitrary data
3572      */
3573     getDragData : function(e){
3574         return this.dragData;
3575     },
3576
3577     // private
3578     onDragEnter : function(e, id){
3579         var target = Roo.dd.DragDropMgr.getDDById(id);
3580         this.cachedTarget = target;
3581         if(this.beforeDragEnter(target, e, id) !== false){
3582             if(target.isNotifyTarget){
3583                 var status = target.notifyEnter(this, e, this.dragData);
3584                 this.proxy.setStatus(status);
3585             }else{
3586                 this.proxy.setStatus(this.dropAllowed);
3587             }
3588             
3589             if(this.afterDragEnter){
3590                 /**
3591                  * An empty function by default, but provided so that you can perform a custom action
3592                  * when the dragged item enters the drop target by providing an implementation.
3593                  * @param {Roo.dd.DragDrop} target The drop target
3594                  * @param {Event} e The event object
3595                  * @param {String} id The id of the dragged element
3596                  * @method afterDragEnter
3597                  */
3598                 this.afterDragEnter(target, e, id);
3599             }
3600         }
3601     },
3602
3603     /**
3604      * An empty function by default, but provided so that you can perform a custom action
3605      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3606      * @param {Roo.dd.DragDrop} target The drop target
3607      * @param {Event} e The event object
3608      * @param {String} id The id of the dragged element
3609      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3610      */
3611     beforeDragEnter : function(target, e, id){
3612         return true;
3613     },
3614
3615     // private
3616     alignElWithMouse: function() {
3617         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3618         this.proxy.sync();
3619     },
3620
3621     // private
3622     onDragOver : function(e, id){
3623         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3624         if(this.beforeDragOver(target, e, id) !== false){
3625             if(target.isNotifyTarget){
3626                 var status = target.notifyOver(this, e, this.dragData);
3627                 this.proxy.setStatus(status);
3628             }
3629
3630             if(this.afterDragOver){
3631                 /**
3632                  * An empty function by default, but provided so that you can perform a custom action
3633                  * while the dragged item is over the drop target by providing an implementation.
3634                  * @param {Roo.dd.DragDrop} target The drop target
3635                  * @param {Event} e The event object
3636                  * @param {String} id The id of the dragged element
3637                  * @method afterDragOver
3638                  */
3639                 this.afterDragOver(target, e, id);
3640             }
3641         }
3642     },
3643
3644     /**
3645      * An empty function by default, but provided so that you can perform a custom action
3646      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3647      * @param {Roo.dd.DragDrop} target The drop target
3648      * @param {Event} e The event object
3649      * @param {String} id The id of the dragged element
3650      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3651      */
3652     beforeDragOver : function(target, e, id){
3653         return true;
3654     },
3655
3656     // private
3657     onDragOut : function(e, id){
3658         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3659         if(this.beforeDragOut(target, e, id) !== false){
3660             if(target.isNotifyTarget){
3661                 target.notifyOut(this, e, this.dragData);
3662             }
3663             this.proxy.reset();
3664             if(this.afterDragOut){
3665                 /**
3666                  * An empty function by default, but provided so that you can perform a custom action
3667                  * after the dragged item is dragged out of the target without dropping.
3668                  * @param {Roo.dd.DragDrop} target The drop target
3669                  * @param {Event} e The event object
3670                  * @param {String} id The id of the dragged element
3671                  * @method afterDragOut
3672                  */
3673                 this.afterDragOut(target, e, id);
3674             }
3675         }
3676         this.cachedTarget = null;
3677     },
3678
3679     /**
3680      * An empty function by default, but provided so that you can perform a custom action before the dragged
3681      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3682      * @param {Roo.dd.DragDrop} target The drop target
3683      * @param {Event} e The event object
3684      * @param {String} id The id of the dragged element
3685      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3686      */
3687     beforeDragOut : function(target, e, id){
3688         return true;
3689     },
3690     
3691     // private
3692     onDragDrop : function(e, id){
3693         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3694         if(this.beforeDragDrop(target, e, id) !== false){
3695             if(target.isNotifyTarget){
3696                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3697                     this.onValidDrop(target, e, id);
3698                 }else{
3699                     this.onInvalidDrop(target, e, id);
3700                 }
3701             }else{
3702                 this.onValidDrop(target, e, id);
3703             }
3704             
3705             if(this.afterDragDrop){
3706                 /**
3707                  * An empty function by default, but provided so that you can perform a custom action
3708                  * after a valid drag drop has occurred by providing an implementation.
3709                  * @param {Roo.dd.DragDrop} target The drop target
3710                  * @param {Event} e The event object
3711                  * @param {String} id The id of the dropped element
3712                  * @method afterDragDrop
3713                  */
3714                 this.afterDragDrop(target, e, id);
3715             }
3716         }
3717         delete this.cachedTarget;
3718     },
3719
3720     /**
3721      * An empty function by default, but provided so that you can perform a custom action before the dragged
3722      * item is dropped onto the target and optionally cancel the onDragDrop.
3723      * @param {Roo.dd.DragDrop} target The drop target
3724      * @param {Event} e The event object
3725      * @param {String} id The id of the dragged element
3726      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3727      */
3728     beforeDragDrop : function(target, e, id){
3729         return true;
3730     },
3731
3732     // private
3733     onValidDrop : function(target, e, id){
3734         this.hideProxy();
3735         if(this.afterValidDrop){
3736             /**
3737              * An empty function by default, but provided so that you can perform a custom action
3738              * after a valid drop has occurred by providing an implementation.
3739              * @param {Object} target The target DD 
3740              * @param {Event} e The event object
3741              * @param {String} id The id of the dropped element
3742              * @method afterInvalidDrop
3743              */
3744             this.afterValidDrop(target, e, id);
3745         }
3746     },
3747
3748     // private
3749     getRepairXY : function(e, data){
3750         return this.el.getXY();  
3751     },
3752
3753     // private
3754     onInvalidDrop : function(target, e, id){
3755         this.beforeInvalidDrop(target, e, id);
3756         if(this.cachedTarget){
3757             if(this.cachedTarget.isNotifyTarget){
3758                 this.cachedTarget.notifyOut(this, e, this.dragData);
3759             }
3760             this.cacheTarget = null;
3761         }
3762         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3763
3764         if(this.afterInvalidDrop){
3765             /**
3766              * An empty function by default, but provided so that you can perform a custom action
3767              * after an invalid drop has occurred by providing an implementation.
3768              * @param {Event} e The event object
3769              * @param {String} id The id of the dropped element
3770              * @method afterInvalidDrop
3771              */
3772             this.afterInvalidDrop(e, id);
3773         }
3774     },
3775
3776     // private
3777     afterRepair : function(){
3778         if(Roo.enableFx){
3779             this.el.highlight(this.hlColor || "c3daf9");
3780         }
3781         this.dragging = false;
3782     },
3783
3784     /**
3785      * An empty function by default, but provided so that you can perform a custom action after an invalid
3786      * drop has occurred.
3787      * @param {Roo.dd.DragDrop} target The drop target
3788      * @param {Event} e The event object
3789      * @param {String} id The id of the dragged element
3790      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3791      */
3792     beforeInvalidDrop : function(target, e, id){
3793         return true;
3794     },
3795
3796     // private
3797     handleMouseDown : function(e){
3798         if(this.dragging) {
3799             return;
3800         }
3801         var data = this.getDragData(e);
3802         if(data && this.onBeforeDrag(data, e) !== false){
3803             this.dragData = data;
3804             this.proxy.stop();
3805             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3806         } 
3807     },
3808
3809     /**
3810      * An empty function by default, but provided so that you can perform a custom action before the initial
3811      * drag event begins and optionally cancel it.
3812      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3813      * @param {Event} e The event object
3814      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3815      */
3816     onBeforeDrag : function(data, e){
3817         return true;
3818     },
3819
3820     /**
3821      * An empty function by default, but provided so that you can perform a custom action once the initial
3822      * drag event has begun.  The drag cannot be canceled from this function.
3823      * @param {Number} x The x position of the click on the dragged object
3824      * @param {Number} y The y position of the click on the dragged object
3825      */
3826     onStartDrag : Roo.emptyFn,
3827
3828     // private - YUI override
3829     startDrag : function(x, y){
3830         this.proxy.reset();
3831         this.dragging = true;
3832         this.proxy.update("");
3833         this.onInitDrag(x, y);
3834         this.proxy.show();
3835     },
3836
3837     // private
3838     onInitDrag : function(x, y){
3839         var clone = this.el.dom.cloneNode(true);
3840         clone.id = Roo.id(); // prevent duplicate ids
3841         this.proxy.update(clone);
3842         this.onStartDrag(x, y);
3843         return true;
3844     },
3845
3846     /**
3847      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3848      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3849      */
3850     getProxy : function(){
3851         return this.proxy;  
3852     },
3853
3854     /**
3855      * Hides the drag source's {@link Roo.dd.StatusProxy}
3856      */
3857     hideProxy : function(){
3858         this.proxy.hide();  
3859         this.proxy.reset(true);
3860         this.dragging = false;
3861     },
3862
3863     // private
3864     triggerCacheRefresh : function(){
3865         Roo.dd.DDM.refreshCache(this.groups);
3866     },
3867
3868     // private - override to prevent hiding
3869     b4EndDrag: function(e) {
3870     },
3871
3872     // private - override to prevent moving
3873     endDrag : function(e){
3874         this.onEndDrag(this.dragData, e);
3875     },
3876
3877     // private
3878     onEndDrag : function(data, e){
3879     },
3880     
3881     // private - pin to cursor
3882     autoOffset : function(x, y) {
3883         this.setDelta(-12, -20);
3884     }    
3885 });/*
3886  * Based on:
3887  * Ext JS Library 1.1.1
3888  * Copyright(c) 2006-2007, Ext JS, LLC.
3889  *
3890  * Originally Released Under LGPL - original licence link has changed is not relivant.
3891  *
3892  * Fork - LGPL
3893  * <script type="text/javascript">
3894  */
3895
3896
3897 /**
3898  * @class Roo.dd.DropTarget
3899  * @extends Roo.dd.DDTarget
3900  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3901  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3902  * @constructor
3903  * @param {String/HTMLElement/Element} el The container element
3904  * @param {Object} config
3905  */
3906 Roo.dd.DropTarget = function(el, config){
3907     this.el = Roo.get(el);
3908     
3909     var listeners = false; ;
3910     if (config && config.listeners) {
3911         listeners= config.listeners;
3912         delete config.listeners;
3913     }
3914     Roo.apply(this, config);
3915     
3916     if(this.containerScroll){
3917         Roo.dd.ScrollManager.register(this.el);
3918     }
3919     this.addEvents( {
3920          /**
3921          * @scope Roo.dd.DropTarget
3922          */
3923          
3924          /**
3925          * @event enter
3926          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3927          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3928          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3929          * 
3930          * IMPORTANT : it should set this.overClass and this.dropAllowed
3931          * 
3932          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3933          * @param {Event} e The event
3934          * @param {Object} data An object containing arbitrary data supplied by the drag source
3935          */
3936         "enter" : true,
3937         
3938          /**
3939          * @event over
3940          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3941          * This method will be called on every mouse movement while the drag source is over the drop target.
3942          * This default implementation simply returns the dropAllowed config value.
3943          * 
3944          * IMPORTANT : it should set this.dropAllowed
3945          * 
3946          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3947          * @param {Event} e The event
3948          * @param {Object} data An object containing arbitrary data supplied by the drag source
3949          
3950          */
3951         "over" : true,
3952         /**
3953          * @event out
3954          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3955          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3956          * overClass (if any) from the drop element.
3957          * 
3958          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959          * @param {Event} e The event
3960          * @param {Object} data An object containing arbitrary data supplied by the drag source
3961          */
3962          "out" : true,
3963          
3964         /**
3965          * @event drop
3966          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3967          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3968          * implementation that does something to process the drop event and returns true so that the drag source's
3969          * repair action does not run.
3970          * 
3971          * IMPORTANT : it should set this.success
3972          * 
3973          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3974          * @param {Event} e The event
3975          * @param {Object} data An object containing arbitrary data supplied by the drag source
3976         */
3977          "drop" : true
3978     });
3979             
3980      
3981     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3982         this.el.dom, 
3983         this.ddGroup || this.group,
3984         {
3985             isTarget: true,
3986             listeners : listeners || {} 
3987            
3988         
3989         }
3990     );
3991
3992 };
3993
3994 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3995     /**
3996      * @cfg {String} overClass
3997      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3998      */
3999      /**
4000      * @cfg {String} ddGroup
4001      * The drag drop group to handle drop events for
4002      */
4003      
4004     /**
4005      * @cfg {String} dropAllowed
4006      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4007      */
4008     dropAllowed : "x-dd-drop-ok",
4009     /**
4010      * @cfg {String} dropNotAllowed
4011      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4012      */
4013     dropNotAllowed : "x-dd-drop-nodrop",
4014     /**
4015      * @cfg {boolean} success
4016      * set this after drop listener.. 
4017      */
4018     success : false,
4019     /**
4020      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4021      * if the drop point is valid for over/enter..
4022      */
4023     valid : false,
4024     // private
4025     isTarget : true,
4026
4027     // private
4028     isNotifyTarget : true,
4029     
4030     /**
4031      * @hide
4032      */
4033     notifyEnter : function(dd, e, data)
4034     {
4035         this.valid = true;
4036         this.fireEvent('enter', dd, e, data);
4037         if(this.overClass){
4038             this.el.addClass(this.overClass);
4039         }
4040         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4041             this.valid ? this.dropAllowed : this.dropNotAllowed
4042         );
4043     },
4044
4045     /**
4046      * @hide
4047      */
4048     notifyOver : function(dd, e, data)
4049     {
4050         this.valid = true;
4051         this.fireEvent('over', dd, e, data);
4052         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4053             this.valid ? this.dropAllowed : this.dropNotAllowed
4054         );
4055     },
4056
4057     /**
4058      * @hide
4059      */
4060     notifyOut : function(dd, e, data)
4061     {
4062         this.fireEvent('out', dd, e, data);
4063         if(this.overClass){
4064             this.el.removeClass(this.overClass);
4065         }
4066     },
4067
4068     /**
4069      * @hide
4070      */
4071     notifyDrop : function(dd, e, data)
4072     {
4073         this.success = false;
4074         this.fireEvent('drop', dd, e, data);
4075         return this.success;
4076     }
4077 });/*
4078  * Based on:
4079  * Ext JS Library 1.1.1
4080  * Copyright(c) 2006-2007, Ext JS, LLC.
4081  *
4082  * Originally Released Under LGPL - original licence link has changed is not relivant.
4083  *
4084  * Fork - LGPL
4085  * <script type="text/javascript">
4086  */
4087
4088
4089 /**
4090  * @class Roo.dd.DragZone
4091  * @extends Roo.dd.DragSource
4092  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4093  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4094  * @constructor
4095  * @param {String/HTMLElement/Element} el The container element
4096  * @param {Object} config
4097  */
4098 Roo.dd.DragZone = function(el, config){
4099     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4100     if(this.containerScroll){
4101         Roo.dd.ScrollManager.register(this.el);
4102     }
4103 };
4104
4105 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4106     /**
4107      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4108      * for auto scrolling during drag operations.
4109      */
4110     /**
4111      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4112      * method after a failed drop (defaults to "c3daf9" - light blue)
4113      */
4114
4115     /**
4116      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4117      * for a valid target to drag based on the mouse down. Override this method
4118      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4119      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4120      * @param {EventObject} e The mouse down event
4121      * @return {Object} The dragData
4122      */
4123     getDragData : function(e){
4124         return Roo.dd.Registry.getHandleFromEvent(e);
4125     },
4126     
4127     /**
4128      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4129      * this.dragData.ddel
4130      * @param {Number} x The x position of the click on the dragged object
4131      * @param {Number} y The y position of the click on the dragged object
4132      * @return {Boolean} true to continue the drag, false to cancel
4133      */
4134     onInitDrag : function(x, y){
4135         this.proxy.update(this.dragData.ddel.cloneNode(true));
4136         this.onStartDrag(x, y);
4137         return true;
4138     },
4139     
4140     /**
4141      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4142      */
4143     afterRepair : function(){
4144         if(Roo.enableFx){
4145             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4146         }
4147         this.dragging = false;
4148     },
4149
4150     /**
4151      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4152      * the XY of this.dragData.ddel
4153      * @param {EventObject} e The mouse up event
4154      * @return {Array} The xy location (e.g. [100, 200])
4155      */
4156     getRepairXY : function(e){
4157         return Roo.Element.fly(this.dragData.ddel).getXY();  
4158     }
4159 });/*
4160  * Based on:
4161  * Ext JS Library 1.1.1
4162  * Copyright(c) 2006-2007, Ext JS, LLC.
4163  *
4164  * Originally Released Under LGPL - original licence link has changed is not relivant.
4165  *
4166  * Fork - LGPL
4167  * <script type="text/javascript">
4168  */
4169 /**
4170  * @class Roo.dd.DropZone
4171  * @extends Roo.dd.DropTarget
4172  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4173  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4174  * @constructor
4175  * @param {String/HTMLElement/Element} el The container element
4176  * @param {Object} config
4177  */
4178 Roo.dd.DropZone = function(el, config){
4179     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4180 };
4181
4182 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4183     /**
4184      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4185      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4186      * provide your own custom lookup.
4187      * @param {Event} e The event
4188      * @return {Object} data The custom data
4189      */
4190     getTargetFromEvent : function(e){
4191         return Roo.dd.Registry.getTargetFromEvent(e);
4192     },
4193
4194     /**
4195      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4196      * that it has registered.  This method has no default implementation and should be overridden to provide
4197      * node-specific processing if necessary.
4198      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4199      * {@link #getTargetFromEvent} for this node)
4200      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4201      * @param {Event} e The event
4202      * @param {Object} data An object containing arbitrary data supplied by the drag source
4203      */
4204     onNodeEnter : function(n, dd, e, data){
4205         
4206     },
4207
4208     /**
4209      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4210      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4211      * overridden to provide the proper feedback.
4212      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4213      * {@link #getTargetFromEvent} for this node)
4214      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4215      * @param {Event} e The event
4216      * @param {Object} data An object containing arbitrary data supplied by the drag source
4217      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4218      * underlying {@link Roo.dd.StatusProxy} can be updated
4219      */
4220     onNodeOver : function(n, dd, e, data){
4221         return this.dropAllowed;
4222     },
4223
4224     /**
4225      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4226      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4227      * node-specific processing if necessary.
4228      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4229      * {@link #getTargetFromEvent} for this node)
4230      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4231      * @param {Event} e The event
4232      * @param {Object} data An object containing arbitrary data supplied by the drag source
4233      */
4234     onNodeOut : function(n, dd, e, data){
4235         
4236     },
4237
4238     /**
4239      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4240      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4241      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4242      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4243      * {@link #getTargetFromEvent} for this node)
4244      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4245      * @param {Event} e The event
4246      * @param {Object} data An object containing arbitrary data supplied by the drag source
4247      * @return {Boolean} True if the drop was valid, else false
4248      */
4249     onNodeDrop : function(n, dd, e, data){
4250         return false;
4251     },
4252
4253     /**
4254      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4255      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4256      * it should be overridden to provide the proper feedback if necessary.
4257      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4258      * @param {Event} e The event
4259      * @param {Object} data An object containing arbitrary data supplied by the drag source
4260      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4261      * underlying {@link Roo.dd.StatusProxy} can be updated
4262      */
4263     onContainerOver : function(dd, e, data){
4264         return this.dropNotAllowed;
4265     },
4266
4267     /**
4268      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4269      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4270      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4271      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4272      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4273      * @param {Event} e The event
4274      * @param {Object} data An object containing arbitrary data supplied by the drag source
4275      * @return {Boolean} True if the drop was valid, else false
4276      */
4277     onContainerDrop : function(dd, e, data){
4278         return false;
4279     },
4280
4281     /**
4282      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4283      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4284      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4285      * you should override this method and provide a custom implementation.
4286      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4287      * @param {Event} e The event
4288      * @param {Object} data An object containing arbitrary data supplied by the drag source
4289      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4290      * underlying {@link Roo.dd.StatusProxy} can be updated
4291      */
4292     notifyEnter : function(dd, e, data){
4293         return this.dropNotAllowed;
4294     },
4295
4296     /**
4297      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4298      * This method will be called on every mouse movement while the drag source is over the drop zone.
4299      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4300      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4301      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4302      * registered node, it will call {@link #onContainerOver}.
4303      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4304      * @param {Event} e The event
4305      * @param {Object} data An object containing arbitrary data supplied by the drag source
4306      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4307      * underlying {@link Roo.dd.StatusProxy} can be updated
4308      */
4309     notifyOver : function(dd, e, data){
4310         var n = this.getTargetFromEvent(e);
4311         if(!n){ // not over valid drop target
4312             if(this.lastOverNode){
4313                 this.onNodeOut(this.lastOverNode, dd, e, data);
4314                 this.lastOverNode = null;
4315             }
4316             return this.onContainerOver(dd, e, data);
4317         }
4318         if(this.lastOverNode != n){
4319             if(this.lastOverNode){
4320                 this.onNodeOut(this.lastOverNode, dd, e, data);
4321             }
4322             this.onNodeEnter(n, dd, e, data);
4323             this.lastOverNode = n;
4324         }
4325         return this.onNodeOver(n, dd, e, data);
4326     },
4327
4328     /**
4329      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4330      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4331      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4332      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4333      * @param {Event} e The event
4334      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4335      */
4336     notifyOut : function(dd, e, data){
4337         if(this.lastOverNode){
4338             this.onNodeOut(this.lastOverNode, dd, e, data);
4339             this.lastOverNode = null;
4340         }
4341     },
4342
4343     /**
4344      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4345      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4346      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4347      * otherwise it will call {@link #onContainerDrop}.
4348      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4349      * @param {Event} e The event
4350      * @param {Object} data An object containing arbitrary data supplied by the drag source
4351      * @return {Boolean} True if the drop was valid, else false
4352      */
4353     notifyDrop : function(dd, e, data){
4354         if(this.lastOverNode){
4355             this.onNodeOut(this.lastOverNode, dd, e, data);
4356             this.lastOverNode = null;
4357         }
4358         var n = this.getTargetFromEvent(e);
4359         return n ?
4360             this.onNodeDrop(n, dd, e, data) :
4361             this.onContainerDrop(dd, e, data);
4362     },
4363
4364     // private
4365     triggerCacheRefresh : function(){
4366         Roo.dd.DDM.refreshCache(this.groups);
4367     }  
4368 });/*
4369  * Based on:
4370  * Ext JS Library 1.1.1
4371  * Copyright(c) 2006-2007, Ext JS, LLC.
4372  *
4373  * Originally Released Under LGPL - original licence link has changed is not relivant.
4374  *
4375  * Fork - LGPL
4376  * <script type="text/javascript">
4377  */
4378
4379
4380 /**
4381  * @class Roo.data.SortTypes
4382  * @singleton
4383  * Defines the default sorting (casting?) comparison functions used when sorting data.
4384  */
4385 Roo.data.SortTypes = {
4386     /**
4387      * Default sort that does nothing
4388      * @param {Mixed} s The value being converted
4389      * @return {Mixed} The comparison value
4390      */
4391     none : function(s){
4392         return s;
4393     },
4394     
4395     /**
4396      * The regular expression used to strip tags
4397      * @type {RegExp}
4398      * @property
4399      */
4400     stripTagsRE : /<\/?[^>]+>/gi,
4401     
4402     /**
4403      * Strips all HTML tags to sort on text only
4404      * @param {Mixed} s The value being converted
4405      * @return {String} The comparison value
4406      */
4407     asText : function(s){
4408         return String(s).replace(this.stripTagsRE, "");
4409     },
4410     
4411     /**
4412      * Strips all HTML tags to sort on text only - Case insensitive
4413      * @param {Mixed} s The value being converted
4414      * @return {String} The comparison value
4415      */
4416     asUCText : function(s){
4417         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4418     },
4419     
4420     /**
4421      * Case insensitive string
4422      * @param {Mixed} s The value being converted
4423      * @return {String} The comparison value
4424      */
4425     asUCString : function(s) {
4426         return String(s).toUpperCase();
4427     },
4428     
4429     /**
4430      * Date sorting
4431      * @param {Mixed} s The value being converted
4432      * @return {Number} The comparison value
4433      */
4434     asDate : function(s) {
4435         if(!s){
4436             return 0;
4437         }
4438         if(s instanceof Date){
4439             return s.getTime();
4440         }
4441         return Date.parse(String(s));
4442     },
4443     
4444     /**
4445      * Float sorting
4446      * @param {Mixed} s The value being converted
4447      * @return {Float} The comparison value
4448      */
4449     asFloat : function(s) {
4450         var val = parseFloat(String(s).replace(/,/g, ""));
4451         if(isNaN(val)) val = 0;
4452         return val;
4453     },
4454     
4455     /**
4456      * Integer sorting
4457      * @param {Mixed} s The value being converted
4458      * @return {Number} The comparison value
4459      */
4460     asInt : function(s) {
4461         var val = parseInt(String(s).replace(/,/g, ""));
4462         if(isNaN(val)) val = 0;
4463         return val;
4464     }
4465 };/*
4466  * Based on:
4467  * Ext JS Library 1.1.1
4468  * Copyright(c) 2006-2007, Ext JS, LLC.
4469  *
4470  * Originally Released Under LGPL - original licence link has changed is not relivant.
4471  *
4472  * Fork - LGPL
4473  * <script type="text/javascript">
4474  */
4475
4476 /**
4477 * @class Roo.data.Record
4478  * Instances of this class encapsulate both record <em>definition</em> information, and record
4479  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4480  * to access Records cached in an {@link Roo.data.Store} object.<br>
4481  * <p>
4482  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4483  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4484  * objects.<br>
4485  * <p>
4486  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4487  * @constructor
4488  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4489  * {@link #create}. The parameters are the same.
4490  * @param {Array} data An associative Array of data values keyed by the field name.
4491  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4492  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4493  * not specified an integer id is generated.
4494  */
4495 Roo.data.Record = function(data, id){
4496     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4497     this.data = data;
4498 };
4499
4500 /**
4501  * Generate a constructor for a specific record layout.
4502  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4503  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4504  * Each field definition object may contain the following properties: <ul>
4505  * <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,
4506  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4507  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4508  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4509  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4510  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4511  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4512  * this may be omitted.</p></li>
4513  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4514  * <ul><li>auto (Default, implies no conversion)</li>
4515  * <li>string</li>
4516  * <li>int</li>
4517  * <li>float</li>
4518  * <li>boolean</li>
4519  * <li>date</li></ul></p></li>
4520  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4521  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4522  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4523  * by the Reader into an object that will be stored in the Record. It is passed the
4524  * following parameters:<ul>
4525  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4526  * </ul></p></li>
4527  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4528  * </ul>
4529  * <br>usage:<br><pre><code>
4530 var TopicRecord = Roo.data.Record.create(
4531     {name: 'title', mapping: 'topic_title'},
4532     {name: 'author', mapping: 'username'},
4533     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4534     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4535     {name: 'lastPoster', mapping: 'user2'},
4536     {name: 'excerpt', mapping: 'post_text'}
4537 );
4538
4539 var myNewRecord = new TopicRecord({
4540     title: 'Do my job please',
4541     author: 'noobie',
4542     totalPosts: 1,
4543     lastPost: new Date(),
4544     lastPoster: 'Animal',
4545     excerpt: 'No way dude!'
4546 });
4547 myStore.add(myNewRecord);
4548 </code></pre>
4549  * @method create
4550  * @static
4551  */
4552 Roo.data.Record.create = function(o){
4553     var f = function(){
4554         f.superclass.constructor.apply(this, arguments);
4555     };
4556     Roo.extend(f, Roo.data.Record);
4557     var p = f.prototype;
4558     p.fields = new Roo.util.MixedCollection(false, function(field){
4559         return field.name;
4560     });
4561     for(var i = 0, len = o.length; i < len; i++){
4562         p.fields.add(new Roo.data.Field(o[i]));
4563     }
4564     f.getField = function(name){
4565         return p.fields.get(name);  
4566     };
4567     return f;
4568 };
4569
4570 Roo.data.Record.AUTO_ID = 1000;
4571 Roo.data.Record.EDIT = 'edit';
4572 Roo.data.Record.REJECT = 'reject';
4573 Roo.data.Record.COMMIT = 'commit';
4574
4575 Roo.data.Record.prototype = {
4576     /**
4577      * Readonly flag - true if this record has been modified.
4578      * @type Boolean
4579      */
4580     dirty : false,
4581     editing : false,
4582     error: null,
4583     modified: null,
4584
4585     // private
4586     join : function(store){
4587         this.store = store;
4588     },
4589
4590     /**
4591      * Set the named field to the specified value.
4592      * @param {String} name The name of the field to set.
4593      * @param {Object} value The value to set the field to.
4594      */
4595     set : function(name, value){
4596         if(this.data[name] == value){
4597             return;
4598         }
4599         this.dirty = true;
4600         if(!this.modified){
4601             this.modified = {};
4602         }
4603         if(typeof this.modified[name] == 'undefined'){
4604             this.modified[name] = this.data[name];
4605         }
4606         this.data[name] = value;
4607         if(!this.editing && this.store){
4608             this.store.afterEdit(this);
4609         }       
4610     },
4611
4612     /**
4613      * Get the value of the named field.
4614      * @param {String} name The name of the field to get the value of.
4615      * @return {Object} The value of the field.
4616      */
4617     get : function(name){
4618         return this.data[name]; 
4619     },
4620
4621     // private
4622     beginEdit : function(){
4623         this.editing = true;
4624         this.modified = {}; 
4625     },
4626
4627     // private
4628     cancelEdit : function(){
4629         this.editing = false;
4630         delete this.modified;
4631     },
4632
4633     // private
4634     endEdit : function(){
4635         this.editing = false;
4636         if(this.dirty && this.store){
4637             this.store.afterEdit(this);
4638         }
4639     },
4640
4641     /**
4642      * Usually called by the {@link Roo.data.Store} which owns the Record.
4643      * Rejects all changes made to the Record since either creation, or the last commit operation.
4644      * Modified fields are reverted to their original values.
4645      * <p>
4646      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4647      * of reject operations.
4648      */
4649     reject : function(){
4650         var m = this.modified;
4651         for(var n in m){
4652             if(typeof m[n] != "function"){
4653                 this.data[n] = m[n];
4654             }
4655         }
4656         this.dirty = false;
4657         delete this.modified;
4658         this.editing = false;
4659         if(this.store){
4660             this.store.afterReject(this);
4661         }
4662     },
4663
4664     /**
4665      * Usually called by the {@link Roo.data.Store} which owns the Record.
4666      * Commits all changes made to the Record since either creation, or the last commit operation.
4667      * <p>
4668      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4669      * of commit operations.
4670      */
4671     commit : function(){
4672         this.dirty = false;
4673         delete this.modified;
4674         this.editing = false;
4675         if(this.store){
4676             this.store.afterCommit(this);
4677         }
4678     },
4679
4680     // private
4681     hasError : function(){
4682         return this.error != null;
4683     },
4684
4685     // private
4686     clearError : function(){
4687         this.error = null;
4688     },
4689
4690     /**
4691      * Creates a copy of this record.
4692      * @param {String} id (optional) A new record id if you don't want to use this record's id
4693      * @return {Record}
4694      */
4695     copy : function(newId) {
4696         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4697     }
4698 };/*
4699  * Based on:
4700  * Ext JS Library 1.1.1
4701  * Copyright(c) 2006-2007, Ext JS, LLC.
4702  *
4703  * Originally Released Under LGPL - original licence link has changed is not relivant.
4704  *
4705  * Fork - LGPL
4706  * <script type="text/javascript">
4707  */
4708
4709
4710
4711 /**
4712  * @class Roo.data.Store
4713  * @extends Roo.util.Observable
4714  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4715  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4716  * <p>
4717  * 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
4718  * has no knowledge of the format of the data returned by the Proxy.<br>
4719  * <p>
4720  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4721  * instances from the data object. These records are cached and made available through accessor functions.
4722  * @constructor
4723  * Creates a new Store.
4724  * @param {Object} config A config object containing the objects needed for the Store to access data,
4725  * and read the data into Records.
4726  */
4727 Roo.data.Store = function(config){
4728     this.data = new Roo.util.MixedCollection(false);
4729     this.data.getKey = function(o){
4730         return o.id;
4731     };
4732     this.baseParams = {};
4733     // private
4734     this.paramNames = {
4735         "start" : "start",
4736         "limit" : "limit",
4737         "sort" : "sort",
4738         "dir" : "dir",
4739         "multisort" : "_multisort"
4740     };
4741
4742     if(config && config.data){
4743         this.inlineData = config.data;
4744         delete config.data;
4745     }
4746
4747     Roo.apply(this, config);
4748     
4749     if(this.reader){ // reader passed
4750         this.reader = Roo.factory(this.reader, Roo.data);
4751         this.reader.xmodule = this.xmodule || false;
4752         if(!this.recordType){
4753             this.recordType = this.reader.recordType;
4754         }
4755         if(this.reader.onMetaChange){
4756             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4757         }
4758     }
4759
4760     if(this.recordType){
4761         this.fields = this.recordType.prototype.fields;
4762     }
4763     this.modified = [];
4764
4765     this.addEvents({
4766         /**
4767          * @event datachanged
4768          * Fires when the data cache has changed, and a widget which is using this Store
4769          * as a Record cache should refresh its view.
4770          * @param {Store} this
4771          */
4772         datachanged : true,
4773         /**
4774          * @event metachange
4775          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4776          * @param {Store} this
4777          * @param {Object} meta The JSON metadata
4778          */
4779         metachange : true,
4780         /**
4781          * @event add
4782          * Fires when Records have been added to the Store
4783          * @param {Store} this
4784          * @param {Roo.data.Record[]} records The array of Records added
4785          * @param {Number} index The index at which the record(s) were added
4786          */
4787         add : true,
4788         /**
4789          * @event remove
4790          * Fires when a Record has been removed from the Store
4791          * @param {Store} this
4792          * @param {Roo.data.Record} record The Record that was removed
4793          * @param {Number} index The index at which the record was removed
4794          */
4795         remove : true,
4796         /**
4797          * @event update
4798          * Fires when a Record has been updated
4799          * @param {Store} this
4800          * @param {Roo.data.Record} record The Record that was updated
4801          * @param {String} operation The update operation being performed.  Value may be one of:
4802          * <pre><code>
4803  Roo.data.Record.EDIT
4804  Roo.data.Record.REJECT
4805  Roo.data.Record.COMMIT
4806          * </code></pre>
4807          */
4808         update : true,
4809         /**
4810          * @event clear
4811          * Fires when the data cache has been cleared.
4812          * @param {Store} this
4813          */
4814         clear : true,
4815         /**
4816          * @event beforeload
4817          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4818          * the load action will be canceled.
4819          * @param {Store} this
4820          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4821          */
4822         beforeload : true,
4823         /**
4824          * @event beforeloadadd
4825          * Fires after a new set of Records has been loaded.
4826          * @param {Store} this
4827          * @param {Roo.data.Record[]} records The Records that were loaded
4828          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4829          */
4830         beforeloadadd : true,
4831         /**
4832          * @event load
4833          * Fires after a new set of Records has been loaded, before they are added to the store.
4834          * @param {Store} this
4835          * @param {Roo.data.Record[]} records The Records that were loaded
4836          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4837          * @params {Object} return from reader
4838          */
4839         load : true,
4840         /**
4841          * @event loadexception
4842          * Fires if an exception occurs in the Proxy during loading.
4843          * Called with the signature of the Proxy's "loadexception" event.
4844          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4845          * 
4846          * @param {Proxy} 
4847          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4848          * @param {Object} load options 
4849          * @param {Object} jsonData from your request (normally this contains the Exception)
4850          */
4851         loadexception : true
4852     });
4853     
4854     if(this.proxy){
4855         this.proxy = Roo.factory(this.proxy, Roo.data);
4856         this.proxy.xmodule = this.xmodule || false;
4857         this.relayEvents(this.proxy,  ["loadexception"]);
4858     }
4859     this.sortToggle = {};
4860     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4861
4862     Roo.data.Store.superclass.constructor.call(this);
4863
4864     if(this.inlineData){
4865         this.loadData(this.inlineData);
4866         delete this.inlineData;
4867     }
4868 };
4869
4870 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4871      /**
4872     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4873     * without a remote query - used by combo/forms at present.
4874     */
4875     
4876     /**
4877     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4878     */
4879     /**
4880     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4881     */
4882     /**
4883     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4884     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4885     */
4886     /**
4887     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4888     * on any HTTP request
4889     */
4890     /**
4891     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4892     */
4893     /**
4894     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4895     */
4896     multiSort: false,
4897     /**
4898     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4899     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4900     */
4901     remoteSort : false,
4902
4903     /**
4904     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4905      * loaded or when a record is removed. (defaults to false).
4906     */
4907     pruneModifiedRecords : false,
4908
4909     // private
4910     lastOptions : null,
4911
4912     /**
4913      * Add Records to the Store and fires the add event.
4914      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4915      */
4916     add : function(records){
4917         records = [].concat(records);
4918         for(var i = 0, len = records.length; i < len; i++){
4919             records[i].join(this);
4920         }
4921         var index = this.data.length;
4922         this.data.addAll(records);
4923         this.fireEvent("add", this, records, index);
4924     },
4925
4926     /**
4927      * Remove a Record from the Store and fires the remove event.
4928      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4929      */
4930     remove : function(record){
4931         var index = this.data.indexOf(record);
4932         this.data.removeAt(index);
4933         if(this.pruneModifiedRecords){
4934             this.modified.remove(record);
4935         }
4936         this.fireEvent("remove", this, record, index);
4937     },
4938
4939     /**
4940      * Remove all Records from the Store and fires the clear event.
4941      */
4942     removeAll : function(){
4943         this.data.clear();
4944         if(this.pruneModifiedRecords){
4945             this.modified = [];
4946         }
4947         this.fireEvent("clear", this);
4948     },
4949
4950     /**
4951      * Inserts Records to the Store at the given index and fires the add event.
4952      * @param {Number} index The start index at which to insert the passed Records.
4953      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4954      */
4955     insert : function(index, records){
4956         records = [].concat(records);
4957         for(var i = 0, len = records.length; i < len; i++){
4958             this.data.insert(index, records[i]);
4959             records[i].join(this);
4960         }
4961         this.fireEvent("add", this, records, index);
4962     },
4963
4964     /**
4965      * Get the index within the cache of the passed Record.
4966      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4967      * @return {Number} The index of the passed Record. Returns -1 if not found.
4968      */
4969     indexOf : function(record){
4970         return this.data.indexOf(record);
4971     },
4972
4973     /**
4974      * Get the index within the cache of the Record with the passed id.
4975      * @param {String} id The id of the Record to find.
4976      * @return {Number} The index of the Record. Returns -1 if not found.
4977      */
4978     indexOfId : function(id){
4979         return this.data.indexOfKey(id);
4980     },
4981
4982     /**
4983      * Get the Record with the specified id.
4984      * @param {String} id The id of the Record to find.
4985      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4986      */
4987     getById : function(id){
4988         return this.data.key(id);
4989     },
4990
4991     /**
4992      * Get the Record at the specified index.
4993      * @param {Number} index The index of the Record to find.
4994      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4995      */
4996     getAt : function(index){
4997         return this.data.itemAt(index);
4998     },
4999
5000     /**
5001      * Returns a range of Records between specified indices.
5002      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5003      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5004      * @return {Roo.data.Record[]} An array of Records
5005      */
5006     getRange : function(start, end){
5007         return this.data.getRange(start, end);
5008     },
5009
5010     // private
5011     storeOptions : function(o){
5012         o = Roo.apply({}, o);
5013         delete o.callback;
5014         delete o.scope;
5015         this.lastOptions = o;
5016     },
5017
5018     /**
5019      * Loads the Record cache from the configured Proxy using the configured Reader.
5020      * <p>
5021      * If using remote paging, then the first load call must specify the <em>start</em>
5022      * and <em>limit</em> properties in the options.params property to establish the initial
5023      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5024      * <p>
5025      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5026      * and this call will return before the new data has been loaded. Perform any post-processing
5027      * in a callback function, or in a "load" event handler.</strong>
5028      * <p>
5029      * @param {Object} options An object containing properties which control loading options:<ul>
5030      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5031      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5032      * passed the following arguments:<ul>
5033      * <li>r : Roo.data.Record[]</li>
5034      * <li>options: Options object from the load call</li>
5035      * <li>success: Boolean success indicator</li></ul></li>
5036      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5037      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5038      * </ul>
5039      */
5040     load : function(options){
5041         options = options || {};
5042         if(this.fireEvent("beforeload", this, options) !== false){
5043             this.storeOptions(options);
5044             var p = Roo.apply(options.params || {}, this.baseParams);
5045             // if meta was not loaded from remote source.. try requesting it.
5046             if (!this.reader.metaFromRemote) {
5047                 p._requestMeta = 1;
5048             }
5049             if(this.sortInfo && this.remoteSort){
5050                 var pn = this.paramNames;
5051                 p[pn["sort"]] = this.sortInfo.field;
5052                 p[pn["dir"]] = this.sortInfo.direction;
5053             }
5054             if (this.multiSort) {
5055                 var pn = this.paramNames;
5056                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5057             }
5058             
5059             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5060         }
5061     },
5062
5063     /**
5064      * Reloads the Record cache from the configured Proxy using the configured Reader and
5065      * the options from the last load operation performed.
5066      * @param {Object} options (optional) An object containing properties which may override the options
5067      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5068      * the most recently used options are reused).
5069      */
5070     reload : function(options){
5071         this.load(Roo.applyIf(options||{}, this.lastOptions));
5072     },
5073
5074     // private
5075     // Called as a callback by the Reader during a load operation.
5076     loadRecords : function(o, options, success){
5077         if(!o || success === false){
5078             if(success !== false){
5079                 this.fireEvent("load", this, [], options, o);
5080             }
5081             if(options.callback){
5082                 options.callback.call(options.scope || this, [], options, false);
5083             }
5084             return;
5085         }
5086         // if data returned failure - throw an exception.
5087         if (o.success === false) {
5088             // show a message if no listener is registered.
5089             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5090                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5091             }
5092             // loadmask wil be hooked into this..
5093             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5094             return;
5095         }
5096         var r = o.records, t = o.totalRecords || r.length;
5097         
5098         this.fireEvent("beforeloadadd", this, r, options, o);
5099         
5100         if(!options || options.add !== true){
5101             if(this.pruneModifiedRecords){
5102                 this.modified = [];
5103             }
5104             for(var i = 0, len = r.length; i < len; i++){
5105                 r[i].join(this);
5106             }
5107             if(this.snapshot){
5108                 this.data = this.snapshot;
5109                 delete this.snapshot;
5110             }
5111             this.data.clear();
5112             this.data.addAll(r);
5113             this.totalLength = t;
5114             this.applySort();
5115             this.fireEvent("datachanged", this);
5116         }else{
5117             this.totalLength = Math.max(t, this.data.length+r.length);
5118             this.add(r);
5119         }
5120         this.fireEvent("load", this, r, options, o);
5121         if(options.callback){
5122             options.callback.call(options.scope || this, r, options, true);
5123         }
5124     },
5125
5126
5127     /**
5128      * Loads data from a passed data block. A Reader which understands the format of the data
5129      * must have been configured in the constructor.
5130      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5131      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5132      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5133      */
5134     loadData : function(o, append){
5135         var r = this.reader.readRecords(o);
5136         this.loadRecords(r, {add: append}, true);
5137     },
5138
5139     /**
5140      * Gets the number of cached records.
5141      * <p>
5142      * <em>If using paging, this may not be the total size of the dataset. If the data object
5143      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5144      * the data set size</em>
5145      */
5146     getCount : function(){
5147         return this.data.length || 0;
5148     },
5149
5150     /**
5151      * Gets the total number of records in the dataset as returned by the server.
5152      * <p>
5153      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5154      * the dataset size</em>
5155      */
5156     getTotalCount : function(){
5157         return this.totalLength || 0;
5158     },
5159
5160     /**
5161      * Returns the sort state of the Store as an object with two properties:
5162      * <pre><code>
5163  field {String} The name of the field by which the Records are sorted
5164  direction {String} The sort order, "ASC" or "DESC"
5165      * </code></pre>
5166      */
5167     getSortState : function(){
5168         return this.sortInfo;
5169     },
5170
5171     // private
5172     applySort : function(){
5173         if(this.sortInfo && !this.remoteSort){
5174             var s = this.sortInfo, f = s.field;
5175             var st = this.fields.get(f).sortType;
5176             var fn = function(r1, r2){
5177                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5178                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5179             };
5180             this.data.sort(s.direction, fn);
5181             if(this.snapshot && this.snapshot != this.data){
5182                 this.snapshot.sort(s.direction, fn);
5183             }
5184         }
5185     },
5186
5187     /**
5188      * Sets the default sort column and order to be used by the next load operation.
5189      * @param {String} fieldName The name of the field to sort by.
5190      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5191      */
5192     setDefaultSort : function(field, dir){
5193         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5194     },
5195
5196     /**
5197      * Sort the Records.
5198      * If remote sorting is used, the sort is performed on the server, and the cache is
5199      * reloaded. If local sorting is used, the cache is sorted internally.
5200      * @param {String} fieldName The name of the field to sort by.
5201      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5202      */
5203     sort : function(fieldName, dir){
5204         var f = this.fields.get(fieldName);
5205         if(!dir){
5206             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5207             
5208             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5209                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5210             }else{
5211                 dir = f.sortDir;
5212             }
5213         }
5214         this.sortToggle[f.name] = dir;
5215         this.sortInfo = {field: f.name, direction: dir};
5216         if(!this.remoteSort){
5217             this.applySort();
5218             this.fireEvent("datachanged", this);
5219         }else{
5220             this.load(this.lastOptions);
5221         }
5222     },
5223
5224     /**
5225      * Calls the specified function for each of the Records in the cache.
5226      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5227      * Returning <em>false</em> aborts and exits the iteration.
5228      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5229      */
5230     each : function(fn, scope){
5231         this.data.each(fn, scope);
5232     },
5233
5234     /**
5235      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5236      * (e.g., during paging).
5237      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5238      */
5239     getModifiedRecords : function(){
5240         return this.modified;
5241     },
5242
5243     // private
5244     createFilterFn : function(property, value, anyMatch){
5245         if(!value.exec){ // not a regex
5246             value = String(value);
5247             if(value.length == 0){
5248                 return false;
5249             }
5250             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5251         }
5252         return function(r){
5253             return value.test(r.data[property]);
5254         };
5255     },
5256
5257     /**
5258      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5259      * @param {String} property A field on your records
5260      * @param {Number} start The record index to start at (defaults to 0)
5261      * @param {Number} end The last record index to include (defaults to length - 1)
5262      * @return {Number} The sum
5263      */
5264     sum : function(property, start, end){
5265         var rs = this.data.items, v = 0;
5266         start = start || 0;
5267         end = (end || end === 0) ? end : rs.length-1;
5268
5269         for(var i = start; i <= end; i++){
5270             v += (rs[i].data[property] || 0);
5271         }
5272         return v;
5273     },
5274
5275     /**
5276      * Filter the records by a specified property.
5277      * @param {String} field A field on your records
5278      * @param {String/RegExp} value Either a string that the field
5279      * should start with or a RegExp to test against the field
5280      * @param {Boolean} anyMatch True to match any part not just the beginning
5281      */
5282     filter : function(property, value, anyMatch){
5283         var fn = this.createFilterFn(property, value, anyMatch);
5284         return fn ? this.filterBy(fn) : this.clearFilter();
5285     },
5286
5287     /**
5288      * Filter by a function. The specified function will be called with each
5289      * record in this data source. If the function returns true the record is included,
5290      * otherwise it is filtered.
5291      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5292      * @param {Object} scope (optional) The scope of the function (defaults to this)
5293      */
5294     filterBy : function(fn, scope){
5295         this.snapshot = this.snapshot || this.data;
5296         this.data = this.queryBy(fn, scope||this);
5297         this.fireEvent("datachanged", this);
5298     },
5299
5300     /**
5301      * Query the records by a specified property.
5302      * @param {String} field A field on your records
5303      * @param {String/RegExp} value Either a string that the field
5304      * should start with or a RegExp to test against the field
5305      * @param {Boolean} anyMatch True to match any part not just the beginning
5306      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5307      */
5308     query : function(property, value, anyMatch){
5309         var fn = this.createFilterFn(property, value, anyMatch);
5310         return fn ? this.queryBy(fn) : this.data.clone();
5311     },
5312
5313     /**
5314      * Query by a function. The specified function will be called with each
5315      * record in this data source. If the function returns true the record is included
5316      * in the results.
5317      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5318      * @param {Object} scope (optional) The scope of the function (defaults to this)
5319       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5320      **/
5321     queryBy : function(fn, scope){
5322         var data = this.snapshot || this.data;
5323         return data.filterBy(fn, scope||this);
5324     },
5325
5326     /**
5327      * Collects unique values for a particular dataIndex from this store.
5328      * @param {String} dataIndex The property to collect
5329      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5330      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5331      * @return {Array} An array of the unique values
5332      **/
5333     collect : function(dataIndex, allowNull, bypassFilter){
5334         var d = (bypassFilter === true && this.snapshot) ?
5335                 this.snapshot.items : this.data.items;
5336         var v, sv, r = [], l = {};
5337         for(var i = 0, len = d.length; i < len; i++){
5338             v = d[i].data[dataIndex];
5339             sv = String(v);
5340             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5341                 l[sv] = true;
5342                 r[r.length] = v;
5343             }
5344         }
5345         return r;
5346     },
5347
5348     /**
5349      * Revert to a view of the Record cache with no filtering applied.
5350      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5351      */
5352     clearFilter : function(suppressEvent){
5353         if(this.snapshot && this.snapshot != this.data){
5354             this.data = this.snapshot;
5355             delete this.snapshot;
5356             if(suppressEvent !== true){
5357                 this.fireEvent("datachanged", this);
5358             }
5359         }
5360     },
5361
5362     // private
5363     afterEdit : function(record){
5364         if(this.modified.indexOf(record) == -1){
5365             this.modified.push(record);
5366         }
5367         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5368     },
5369     
5370     // private
5371     afterReject : function(record){
5372         this.modified.remove(record);
5373         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5374     },
5375
5376     // private
5377     afterCommit : function(record){
5378         this.modified.remove(record);
5379         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5380     },
5381
5382     /**
5383      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5384      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5385      */
5386     commitChanges : function(){
5387         var m = this.modified.slice(0);
5388         this.modified = [];
5389         for(var i = 0, len = m.length; i < len; i++){
5390             m[i].commit();
5391         }
5392     },
5393
5394     /**
5395      * Cancel outstanding changes on all changed records.
5396      */
5397     rejectChanges : function(){
5398         var m = this.modified.slice(0);
5399         this.modified = [];
5400         for(var i = 0, len = m.length; i < len; i++){
5401             m[i].reject();
5402         }
5403     },
5404
5405     onMetaChange : function(meta, rtype, o){
5406         this.recordType = rtype;
5407         this.fields = rtype.prototype.fields;
5408         delete this.snapshot;
5409         this.sortInfo = meta.sortInfo || this.sortInfo;
5410         this.modified = [];
5411         this.fireEvent('metachange', this, this.reader.meta);
5412     }
5413 });/*
5414  * Based on:
5415  * Ext JS Library 1.1.1
5416  * Copyright(c) 2006-2007, Ext JS, LLC.
5417  *
5418  * Originally Released Under LGPL - original licence link has changed is not relivant.
5419  *
5420  * Fork - LGPL
5421  * <script type="text/javascript">
5422  */
5423
5424 /**
5425  * @class Roo.data.SimpleStore
5426  * @extends Roo.data.Store
5427  * Small helper class to make creating Stores from Array data easier.
5428  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5429  * @cfg {Array} fields An array of field definition objects, or field name strings.
5430  * @cfg {Array} data The multi-dimensional array of data
5431  * @constructor
5432  * @param {Object} config
5433  */
5434 Roo.data.SimpleStore = function(config){
5435     Roo.data.SimpleStore.superclass.constructor.call(this, {
5436         isLocal : true,
5437         reader: new Roo.data.ArrayReader({
5438                 id: config.id
5439             },
5440             Roo.data.Record.create(config.fields)
5441         ),
5442         proxy : new Roo.data.MemoryProxy(config.data)
5443     });
5444     this.load();
5445 };
5446 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5447  * Based on:
5448  * Ext JS Library 1.1.1
5449  * Copyright(c) 2006-2007, Ext JS, LLC.
5450  *
5451  * Originally Released Under LGPL - original licence link has changed is not relivant.
5452  *
5453  * Fork - LGPL
5454  * <script type="text/javascript">
5455  */
5456
5457 /**
5458 /**
5459  * @extends Roo.data.Store
5460  * @class Roo.data.JsonStore
5461  * Small helper class to make creating Stores for JSON data easier. <br/>
5462 <pre><code>
5463 var store = new Roo.data.JsonStore({
5464     url: 'get-images.php',
5465     root: 'images',
5466     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5467 });
5468 </code></pre>
5469  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5470  * JsonReader and HttpProxy (unless inline data is provided).</b>
5471  * @cfg {Array} fields An array of field definition objects, or field name strings.
5472  * @constructor
5473  * @param {Object} config
5474  */
5475 Roo.data.JsonStore = function(c){
5476     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5477         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5478         reader: new Roo.data.JsonReader(c, c.fields)
5479     }));
5480 };
5481 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5482  * Based on:
5483  * Ext JS Library 1.1.1
5484  * Copyright(c) 2006-2007, Ext JS, LLC.
5485  *
5486  * Originally Released Under LGPL - original licence link has changed is not relivant.
5487  *
5488  * Fork - LGPL
5489  * <script type="text/javascript">
5490  */
5491
5492  
5493 Roo.data.Field = function(config){
5494     if(typeof config == "string"){
5495         config = {name: config};
5496     }
5497     Roo.apply(this, config);
5498     
5499     if(!this.type){
5500         this.type = "auto";
5501     }
5502     
5503     var st = Roo.data.SortTypes;
5504     // named sortTypes are supported, here we look them up
5505     if(typeof this.sortType == "string"){
5506         this.sortType = st[this.sortType];
5507     }
5508     
5509     // set default sortType for strings and dates
5510     if(!this.sortType){
5511         switch(this.type){
5512             case "string":
5513                 this.sortType = st.asUCString;
5514                 break;
5515             case "date":
5516                 this.sortType = st.asDate;
5517                 break;
5518             default:
5519                 this.sortType = st.none;
5520         }
5521     }
5522
5523     // define once
5524     var stripRe = /[\$,%]/g;
5525
5526     // prebuilt conversion function for this field, instead of
5527     // switching every time we're reading a value
5528     if(!this.convert){
5529         var cv, dateFormat = this.dateFormat;
5530         switch(this.type){
5531             case "":
5532             case "auto":
5533             case undefined:
5534                 cv = function(v){ return v; };
5535                 break;
5536             case "string":
5537                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5538                 break;
5539             case "int":
5540                 cv = function(v){
5541                     return v !== undefined && v !== null && v !== '' ?
5542                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5543                     };
5544                 break;
5545             case "float":
5546                 cv = function(v){
5547                     return v !== undefined && v !== null && v !== '' ?
5548                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5549                     };
5550                 break;
5551             case "bool":
5552             case "boolean":
5553                 cv = function(v){ return v === true || v === "true" || v == 1; };
5554                 break;
5555             case "date":
5556                 cv = function(v){
5557                     if(!v){
5558                         return '';
5559                     }
5560                     if(v instanceof Date){
5561                         return v;
5562                     }
5563                     if(dateFormat){
5564                         if(dateFormat == "timestamp"){
5565                             return new Date(v*1000);
5566                         }
5567                         return Date.parseDate(v, dateFormat);
5568                     }
5569                     var parsed = Date.parse(v);
5570                     return parsed ? new Date(parsed) : null;
5571                 };
5572              break;
5573             
5574         }
5575         this.convert = cv;
5576     }
5577 };
5578
5579 Roo.data.Field.prototype = {
5580     dateFormat: null,
5581     defaultValue: "",
5582     mapping: null,
5583     sortType : null,
5584     sortDir : "ASC"
5585 };/*
5586  * Based on:
5587  * Ext JS Library 1.1.1
5588  * Copyright(c) 2006-2007, Ext JS, LLC.
5589  *
5590  * Originally Released Under LGPL - original licence link has changed is not relivant.
5591  *
5592  * Fork - LGPL
5593  * <script type="text/javascript">
5594  */
5595  
5596 // Base class for reading structured data from a data source.  This class is intended to be
5597 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5598
5599 /**
5600  * @class Roo.data.DataReader
5601  * Base class for reading structured data from a data source.  This class is intended to be
5602  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5603  */
5604
5605 Roo.data.DataReader = function(meta, recordType){
5606     
5607     this.meta = meta;
5608     
5609     this.recordType = recordType instanceof Array ? 
5610         Roo.data.Record.create(recordType) : recordType;
5611 };
5612
5613 Roo.data.DataReader.prototype = {
5614      /**
5615      * Create an empty record
5616      * @param {Object} data (optional) - overlay some values
5617      * @return {Roo.data.Record} record created.
5618      */
5619     newRow :  function(d) {
5620         var da =  {};
5621         this.recordType.prototype.fields.each(function(c) {
5622             switch( c.type) {
5623                 case 'int' : da[c.name] = 0; break;
5624                 case 'date' : da[c.name] = new Date(); break;
5625                 case 'float' : da[c.name] = 0.0; break;
5626                 case 'boolean' : da[c.name] = false; break;
5627                 default : da[c.name] = ""; break;
5628             }
5629             
5630         });
5631         return new this.recordType(Roo.apply(da, d));
5632     }
5633     
5634 };/*
5635  * Based on:
5636  * Ext JS Library 1.1.1
5637  * Copyright(c) 2006-2007, Ext JS, LLC.
5638  *
5639  * Originally Released Under LGPL - original licence link has changed is not relivant.
5640  *
5641  * Fork - LGPL
5642  * <script type="text/javascript">
5643  */
5644
5645 /**
5646  * @class Roo.data.DataProxy
5647  * @extends Roo.data.Observable
5648  * This class is an abstract base class for implementations which provide retrieval of
5649  * unformatted data objects.<br>
5650  * <p>
5651  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5652  * (of the appropriate type which knows how to parse the data object) to provide a block of
5653  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5654  * <p>
5655  * Custom implementations must implement the load method as described in
5656  * {@link Roo.data.HttpProxy#load}.
5657  */
5658 Roo.data.DataProxy = function(){
5659     this.addEvents({
5660         /**
5661          * @event beforeload
5662          * Fires before a network request is made to retrieve a data object.
5663          * @param {Object} This DataProxy object.
5664          * @param {Object} params The params parameter to the load function.
5665          */
5666         beforeload : true,
5667         /**
5668          * @event load
5669          * Fires before the load method's callback is called.
5670          * @param {Object} This DataProxy object.
5671          * @param {Object} o The data object.
5672          * @param {Object} arg The callback argument object passed to the load function.
5673          */
5674         load : true,
5675         /**
5676          * @event loadexception
5677          * Fires if an Exception occurs during data retrieval.
5678          * @param {Object} This DataProxy object.
5679          * @param {Object} o The data object.
5680          * @param {Object} arg The callback argument object passed to the load function.
5681          * @param {Object} e The Exception.
5682          */
5683         loadexception : true
5684     });
5685     Roo.data.DataProxy.superclass.constructor.call(this);
5686 };
5687
5688 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5689
5690     /**
5691      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5692      */
5693 /*
5694  * Based on:
5695  * Ext JS Library 1.1.1
5696  * Copyright(c) 2006-2007, Ext JS, LLC.
5697  *
5698  * Originally Released Under LGPL - original licence link has changed is not relivant.
5699  *
5700  * Fork - LGPL
5701  * <script type="text/javascript">
5702  */
5703 /**
5704  * @class Roo.data.MemoryProxy
5705  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5706  * to the Reader when its load method is called.
5707  * @constructor
5708  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5709  */
5710 Roo.data.MemoryProxy = function(data){
5711     if (data.data) {
5712         data = data.data;
5713     }
5714     Roo.data.MemoryProxy.superclass.constructor.call(this);
5715     this.data = data;
5716 };
5717
5718 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5719     /**
5720      * Load data from the requested source (in this case an in-memory
5721      * data object passed to the constructor), read the data object into
5722      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5723      * process that block using the passed callback.
5724      * @param {Object} params This parameter is not used by the MemoryProxy class.
5725      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5726      * object into a block of Roo.data.Records.
5727      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5728      * The function must be passed <ul>
5729      * <li>The Record block object</li>
5730      * <li>The "arg" argument from the load function</li>
5731      * <li>A boolean success indicator</li>
5732      * </ul>
5733      * @param {Object} scope The scope in which to call the callback
5734      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5735      */
5736     load : function(params, reader, callback, scope, arg){
5737         params = params || {};
5738         var result;
5739         try {
5740             result = reader.readRecords(this.data);
5741         }catch(e){
5742             this.fireEvent("loadexception", this, arg, null, e);
5743             callback.call(scope, null, arg, false);
5744             return;
5745         }
5746         callback.call(scope, result, arg, true);
5747     },
5748     
5749     // private
5750     update : function(params, records){
5751         
5752     }
5753 });/*
5754  * Based on:
5755  * Ext JS Library 1.1.1
5756  * Copyright(c) 2006-2007, Ext JS, LLC.
5757  *
5758  * Originally Released Under LGPL - original licence link has changed is not relivant.
5759  *
5760  * Fork - LGPL
5761  * <script type="text/javascript">
5762  */
5763 /**
5764  * @class Roo.data.HttpProxy
5765  * @extends Roo.data.DataProxy
5766  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5767  * configured to reference a certain URL.<br><br>
5768  * <p>
5769  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5770  * from which the running page was served.<br><br>
5771  * <p>
5772  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5773  * <p>
5774  * Be aware that to enable the browser to parse an XML document, the server must set
5775  * the Content-Type header in the HTTP response to "text/xml".
5776  * @constructor
5777  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5778  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5779  * will be used to make the request.
5780  */
5781 Roo.data.HttpProxy = function(conn){
5782     Roo.data.HttpProxy.superclass.constructor.call(this);
5783     // is conn a conn config or a real conn?
5784     this.conn = conn;
5785     this.useAjax = !conn || !conn.events;
5786   
5787 };
5788
5789 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5790     // thse are take from connection...
5791     
5792     /**
5793      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5794      */
5795     /**
5796      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5797      * extra parameters to each request made by this object. (defaults to undefined)
5798      */
5799     /**
5800      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5801      *  to each request made by this object. (defaults to undefined)
5802      */
5803     /**
5804      * @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)
5805      */
5806     /**
5807      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5808      */
5809      /**
5810      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5811      * @type Boolean
5812      */
5813   
5814
5815     /**
5816      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5817      * @type Boolean
5818      */
5819     /**
5820      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5821      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5822      * a finer-grained basis than the DataProxy events.
5823      */
5824     getConnection : function(){
5825         return this.useAjax ? Roo.Ajax : this.conn;
5826     },
5827
5828     /**
5829      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5830      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5831      * process that block using the passed callback.
5832      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5833      * for the request to the remote server.
5834      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5835      * object into a block of Roo.data.Records.
5836      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5837      * The function must be passed <ul>
5838      * <li>The Record block object</li>
5839      * <li>The "arg" argument from the load function</li>
5840      * <li>A boolean success indicator</li>
5841      * </ul>
5842      * @param {Object} scope The scope in which to call the callback
5843      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5844      */
5845     load : function(params, reader, callback, scope, arg){
5846         if(this.fireEvent("beforeload", this, params) !== false){
5847             var  o = {
5848                 params : params || {},
5849                 request: {
5850                     callback : callback,
5851                     scope : scope,
5852                     arg : arg
5853                 },
5854                 reader: reader,
5855                 callback : this.loadResponse,
5856                 scope: this
5857             };
5858             if(this.useAjax){
5859                 Roo.applyIf(o, this.conn);
5860                 if(this.activeRequest){
5861                     Roo.Ajax.abort(this.activeRequest);
5862                 }
5863                 this.activeRequest = Roo.Ajax.request(o);
5864             }else{
5865                 this.conn.request(o);
5866             }
5867         }else{
5868             callback.call(scope||this, null, arg, false);
5869         }
5870     },
5871
5872     // private
5873     loadResponse : function(o, success, response){
5874         delete this.activeRequest;
5875         if(!success){
5876             this.fireEvent("loadexception", this, o, response);
5877             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5878             return;
5879         }
5880         var result;
5881         try {
5882             result = o.reader.read(response);
5883         }catch(e){
5884             this.fireEvent("loadexception", this, o, response, e);
5885             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5886             return;
5887         }
5888         
5889         this.fireEvent("load", this, o, o.request.arg);
5890         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5891     },
5892
5893     // private
5894     update : function(dataSet){
5895
5896     },
5897
5898     // private
5899     updateResponse : function(dataSet){
5900
5901     }
5902 });/*
5903  * Based on:
5904  * Ext JS Library 1.1.1
5905  * Copyright(c) 2006-2007, Ext JS, LLC.
5906  *
5907  * Originally Released Under LGPL - original licence link has changed is not relivant.
5908  *
5909  * Fork - LGPL
5910  * <script type="text/javascript">
5911  */
5912
5913 /**
5914  * @class Roo.data.ScriptTagProxy
5915  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5916  * other than the originating domain of the running page.<br><br>
5917  * <p>
5918  * <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
5919  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5920  * <p>
5921  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5922  * source code that is used as the source inside a &lt;script> tag.<br><br>
5923  * <p>
5924  * In order for the browser to process the returned data, the server must wrap the data object
5925  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5926  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5927  * depending on whether the callback name was passed:
5928  * <p>
5929  * <pre><code>
5930 boolean scriptTag = false;
5931 String cb = request.getParameter("callback");
5932 if (cb != null) {
5933     scriptTag = true;
5934     response.setContentType("text/javascript");
5935 } else {
5936     response.setContentType("application/x-json");
5937 }
5938 Writer out = response.getWriter();
5939 if (scriptTag) {
5940     out.write(cb + "(");
5941 }
5942 out.print(dataBlock.toJsonString());
5943 if (scriptTag) {
5944     out.write(");");
5945 }
5946 </pre></code>
5947  *
5948  * @constructor
5949  * @param {Object} config A configuration object.
5950  */
5951 Roo.data.ScriptTagProxy = function(config){
5952     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5953     Roo.apply(this, config);
5954     this.head = document.getElementsByTagName("head")[0];
5955 };
5956
5957 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5958
5959 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5960     /**
5961      * @cfg {String} url The URL from which to request the data object.
5962      */
5963     /**
5964      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5965      */
5966     timeout : 30000,
5967     /**
5968      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5969      * the server the name of the callback function set up by the load call to process the returned data object.
5970      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5971      * javascript output which calls this named function passing the data object as its only parameter.
5972      */
5973     callbackParam : "callback",
5974     /**
5975      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5976      * name to the request.
5977      */
5978     nocache : true,
5979
5980     /**
5981      * Load data from the configured URL, read the data object into
5982      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5983      * process that block using the passed callback.
5984      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5985      * for the request to the remote server.
5986      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5987      * object into a block of Roo.data.Records.
5988      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5989      * The function must be passed <ul>
5990      * <li>The Record block object</li>
5991      * <li>The "arg" argument from the load function</li>
5992      * <li>A boolean success indicator</li>
5993      * </ul>
5994      * @param {Object} scope The scope in which to call the callback
5995      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5996      */
5997     load : function(params, reader, callback, scope, arg){
5998         if(this.fireEvent("beforeload", this, params) !== false){
5999
6000             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6001
6002             var url = this.url;
6003             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6004             if(this.nocache){
6005                 url += "&_dc=" + (new Date().getTime());
6006             }
6007             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6008             var trans = {
6009                 id : transId,
6010                 cb : "stcCallback"+transId,
6011                 scriptId : "stcScript"+transId,
6012                 params : params,
6013                 arg : arg,
6014                 url : url,
6015                 callback : callback,
6016                 scope : scope,
6017                 reader : reader
6018             };
6019             var conn = this;
6020
6021             window[trans.cb] = function(o){
6022                 conn.handleResponse(o, trans);
6023             };
6024
6025             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6026
6027             if(this.autoAbort !== false){
6028                 this.abort();
6029             }
6030
6031             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6032
6033             var script = document.createElement("script");
6034             script.setAttribute("src", url);
6035             script.setAttribute("type", "text/javascript");
6036             script.setAttribute("id", trans.scriptId);
6037             this.head.appendChild(script);
6038
6039             this.trans = trans;
6040         }else{
6041             callback.call(scope||this, null, arg, false);
6042         }
6043     },
6044
6045     // private
6046     isLoading : function(){
6047         return this.trans ? true : false;
6048     },
6049
6050     /**
6051      * Abort the current server request.
6052      */
6053     abort : function(){
6054         if(this.isLoading()){
6055             this.destroyTrans(this.trans);
6056         }
6057     },
6058
6059     // private
6060     destroyTrans : function(trans, isLoaded){
6061         this.head.removeChild(document.getElementById(trans.scriptId));
6062         clearTimeout(trans.timeoutId);
6063         if(isLoaded){
6064             window[trans.cb] = undefined;
6065             try{
6066                 delete window[trans.cb];
6067             }catch(e){}
6068         }else{
6069             // if hasn't been loaded, wait for load to remove it to prevent script error
6070             window[trans.cb] = function(){
6071                 window[trans.cb] = undefined;
6072                 try{
6073                     delete window[trans.cb];
6074                 }catch(e){}
6075             };
6076         }
6077     },
6078
6079     // private
6080     handleResponse : function(o, trans){
6081         this.trans = false;
6082         this.destroyTrans(trans, true);
6083         var result;
6084         try {
6085             result = trans.reader.readRecords(o);
6086         }catch(e){
6087             this.fireEvent("loadexception", this, o, trans.arg, e);
6088             trans.callback.call(trans.scope||window, null, trans.arg, false);
6089             return;
6090         }
6091         this.fireEvent("load", this, o, trans.arg);
6092         trans.callback.call(trans.scope||window, result, trans.arg, true);
6093     },
6094
6095     // private
6096     handleFailure : function(trans){
6097         this.trans = false;
6098         this.destroyTrans(trans, false);
6099         this.fireEvent("loadexception", this, null, trans.arg);
6100         trans.callback.call(trans.scope||window, null, trans.arg, false);
6101     }
6102 });/*
6103  * Based on:
6104  * Ext JS Library 1.1.1
6105  * Copyright(c) 2006-2007, Ext JS, LLC.
6106  *
6107  * Originally Released Under LGPL - original licence link has changed is not relivant.
6108  *
6109  * Fork - LGPL
6110  * <script type="text/javascript">
6111  */
6112
6113 /**
6114  * @class Roo.data.JsonReader
6115  * @extends Roo.data.DataReader
6116  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6117  * based on mappings in a provided Roo.data.Record constructor.
6118  * 
6119  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6120  * in the reply previously. 
6121  * 
6122  * <p>
6123  * Example code:
6124  * <pre><code>
6125 var RecordDef = Roo.data.Record.create([
6126     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6127     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6128 ]);
6129 var myReader = new Roo.data.JsonReader({
6130     totalProperty: "results",    // The property which contains the total dataset size (optional)
6131     root: "rows",                // The property which contains an Array of row objects
6132     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6133 }, RecordDef);
6134 </code></pre>
6135  * <p>
6136  * This would consume a JSON file like this:
6137  * <pre><code>
6138 { 'results': 2, 'rows': [
6139     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6140     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6141 }
6142 </code></pre>
6143  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6144  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6145  * paged from the remote server.
6146  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6147  * @cfg {String} root name of the property which contains the Array of row objects.
6148  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6149  * @constructor
6150  * Create a new JsonReader
6151  * @param {Object} meta Metadata configuration options
6152  * @param {Object} recordType Either an Array of field definition objects,
6153  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6154  */
6155 Roo.data.JsonReader = function(meta, recordType){
6156     
6157     meta = meta || {};
6158     // set some defaults:
6159     Roo.applyIf(meta, {
6160         totalProperty: 'total',
6161         successProperty : 'success',
6162         root : 'data',
6163         id : 'id'
6164     });
6165     
6166     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6167 };
6168 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6169     
6170     /**
6171      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6172      * Used by Store query builder to append _requestMeta to params.
6173      * 
6174      */
6175     metaFromRemote : false,
6176     /**
6177      * This method is only used by a DataProxy which has retrieved data from a remote server.
6178      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6179      * @return {Object} data A data block which is used by an Roo.data.Store object as
6180      * a cache of Roo.data.Records.
6181      */
6182     read : function(response){
6183         var json = response.responseText;
6184        
6185         var o = /* eval:var:o */ eval("("+json+")");
6186         if(!o) {
6187             throw {message: "JsonReader.read: Json object not found"};
6188         }
6189         
6190         if(o.metaData){
6191             
6192             delete this.ef;
6193             this.metaFromRemote = true;
6194             this.meta = o.metaData;
6195             this.recordType = Roo.data.Record.create(o.metaData.fields);
6196             this.onMetaChange(this.meta, this.recordType, o);
6197         }
6198         return this.readRecords(o);
6199     },
6200
6201     // private function a store will implement
6202     onMetaChange : function(meta, recordType, o){
6203
6204     },
6205
6206     /**
6207          * @ignore
6208          */
6209     simpleAccess: function(obj, subsc) {
6210         return obj[subsc];
6211     },
6212
6213         /**
6214          * @ignore
6215          */
6216     getJsonAccessor: function(){
6217         var re = /[\[\.]/;
6218         return function(expr) {
6219             try {
6220                 return(re.test(expr))
6221                     ? new Function("obj", "return obj." + expr)
6222                     : function(obj){
6223                         return obj[expr];
6224                     };
6225             } catch(e){}
6226             return Roo.emptyFn;
6227         };
6228     }(),
6229
6230     /**
6231      * Create a data block containing Roo.data.Records from an XML document.
6232      * @param {Object} o An object which contains an Array of row objects in the property specified
6233      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6234      * which contains the total size of the dataset.
6235      * @return {Object} data A data block which is used by an Roo.data.Store object as
6236      * a cache of Roo.data.Records.
6237      */
6238     readRecords : function(o){
6239         /**
6240          * After any data loads, the raw JSON data is available for further custom processing.
6241          * @type Object
6242          */
6243         this.o = o;
6244         var s = this.meta, Record = this.recordType,
6245             f = Record.prototype.fields, fi = f.items, fl = f.length;
6246
6247 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6248         if (!this.ef) {
6249             if(s.totalProperty) {
6250                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6251                 }
6252                 if(s.successProperty) {
6253                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6254                 }
6255                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6256                 if (s.id) {
6257                         var g = this.getJsonAccessor(s.id);
6258                         this.getId = function(rec) {
6259                                 var r = g(rec);
6260                                 return (r === undefined || r === "") ? null : r;
6261                         };
6262                 } else {
6263                         this.getId = function(){return null;};
6264                 }
6265             this.ef = [];
6266             for(var jj = 0; jj < fl; jj++){
6267                 f = fi[jj];
6268                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6269                 this.ef[jj] = this.getJsonAccessor(map);
6270             }
6271         }
6272
6273         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6274         if(s.totalProperty){
6275             var vt = parseInt(this.getTotal(o), 10);
6276             if(!isNaN(vt)){
6277                 totalRecords = vt;
6278             }
6279         }
6280         if(s.successProperty){
6281             var vs = this.getSuccess(o);
6282             if(vs === false || vs === 'false'){
6283                 success = false;
6284             }
6285         }
6286         var records = [];
6287             for(var i = 0; i < c; i++){
6288                     var n = root[i];
6289                 var values = {};
6290                 var id = this.getId(n);
6291                 for(var j = 0; j < fl; j++){
6292                     f = fi[j];
6293                 var v = this.ef[j](n);
6294                 if (!f.convert) {
6295                     Roo.log('missing convert for ' + f.name);
6296                     Roo.log(f);
6297                     continue;
6298                 }
6299                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6300                 }
6301                 var record = new Record(values, id);
6302                 record.json = n;
6303                 records[i] = record;
6304             }
6305             return {
6306             raw : o,
6307                 success : success,
6308                 records : records,
6309                 totalRecords : totalRecords
6310             };
6311     }
6312 });/*
6313  * Based on:
6314  * Ext JS Library 1.1.1
6315  * Copyright(c) 2006-2007, Ext JS, LLC.
6316  *
6317  * Originally Released Under LGPL - original licence link has changed is not relivant.
6318  *
6319  * Fork - LGPL
6320  * <script type="text/javascript">
6321  */
6322
6323 /**
6324  * @class Roo.data.XmlReader
6325  * @extends Roo.data.DataReader
6326  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6327  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6328  * <p>
6329  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6330  * header in the HTTP response must be set to "text/xml".</em>
6331  * <p>
6332  * Example code:
6333  * <pre><code>
6334 var RecordDef = Roo.data.Record.create([
6335    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6336    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6337 ]);
6338 var myReader = new Roo.data.XmlReader({
6339    totalRecords: "results", // The element which contains the total dataset size (optional)
6340    record: "row",           // The repeated element which contains row information
6341    id: "id"                 // The element within the row that provides an ID for the record (optional)
6342 }, RecordDef);
6343 </code></pre>
6344  * <p>
6345  * This would consume an XML file like this:
6346  * <pre><code>
6347 &lt;?xml?>
6348 &lt;dataset>
6349  &lt;results>2&lt;/results>
6350  &lt;row>
6351    &lt;id>1&lt;/id>
6352    &lt;name>Bill&lt;/name>
6353    &lt;occupation>Gardener&lt;/occupation>
6354  &lt;/row>
6355  &lt;row>
6356    &lt;id>2&lt;/id>
6357    &lt;name>Ben&lt;/name>
6358    &lt;occupation>Horticulturalist&lt;/occupation>
6359  &lt;/row>
6360 &lt;/dataset>
6361 </code></pre>
6362  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6363  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6364  * paged from the remote server.
6365  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6366  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6367  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6368  * a record identifier value.
6369  * @constructor
6370  * Create a new XmlReader
6371  * @param {Object} meta Metadata configuration options
6372  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6373  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6374  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6375  */
6376 Roo.data.XmlReader = function(meta, recordType){
6377     meta = meta || {};
6378     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6379 };
6380 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6381     /**
6382      * This method is only used by a DataProxy which has retrieved data from a remote server.
6383          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6384          * to contain a method called 'responseXML' that returns an XML document object.
6385      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6386      * a cache of Roo.data.Records.
6387      */
6388     read : function(response){
6389         var doc = response.responseXML;
6390         if(!doc) {
6391             throw {message: "XmlReader.read: XML Document not available"};
6392         }
6393         return this.readRecords(doc);
6394     },
6395
6396     /**
6397      * Create a data block containing Roo.data.Records from an XML document.
6398          * @param {Object} doc A parsed XML document.
6399      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6400      * a cache of Roo.data.Records.
6401      */
6402     readRecords : function(doc){
6403         /**
6404          * After any data loads/reads, the raw XML Document is available for further custom processing.
6405          * @type XMLDocument
6406          */
6407         this.xmlData = doc;
6408         var root = doc.documentElement || doc;
6409         var q = Roo.DomQuery;
6410         var recordType = this.recordType, fields = recordType.prototype.fields;
6411         var sid = this.meta.id;
6412         var totalRecords = 0, success = true;
6413         if(this.meta.totalRecords){
6414             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6415         }
6416         
6417         if(this.meta.success){
6418             var sv = q.selectValue(this.meta.success, root, true);
6419             success = sv !== false && sv !== 'false';
6420         }
6421         var records = [];
6422         var ns = q.select(this.meta.record, root);
6423         for(var i = 0, len = ns.length; i < len; i++) {
6424                 var n = ns[i];
6425                 var values = {};
6426                 var id = sid ? q.selectValue(sid, n) : undefined;
6427                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6428                     var f = fields.items[j];
6429                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6430                     v = f.convert(v);
6431                     values[f.name] = v;
6432                 }
6433                 var record = new recordType(values, id);
6434                 record.node = n;
6435                 records[records.length] = record;
6436             }
6437
6438             return {
6439                 success : success,
6440                 records : records,
6441                 totalRecords : totalRecords || records.length
6442             };
6443     }
6444 });/*
6445  * Based on:
6446  * Ext JS Library 1.1.1
6447  * Copyright(c) 2006-2007, Ext JS, LLC.
6448  *
6449  * Originally Released Under LGPL - original licence link has changed is not relivant.
6450  *
6451  * Fork - LGPL
6452  * <script type="text/javascript">
6453  */
6454
6455 /**
6456  * @class Roo.data.ArrayReader
6457  * @extends Roo.data.DataReader
6458  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6459  * Each element of that Array represents a row of data fields. The
6460  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6461  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6462  * <p>
6463  * Example code:.
6464  * <pre><code>
6465 var RecordDef = Roo.data.Record.create([
6466     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6467     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6468 ]);
6469 var myReader = new Roo.data.ArrayReader({
6470     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6471 }, RecordDef);
6472 </code></pre>
6473  * <p>
6474  * This would consume an Array like this:
6475  * <pre><code>
6476 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6477   </code></pre>
6478  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6479  * @constructor
6480  * Create a new JsonReader
6481  * @param {Object} meta Metadata configuration options.
6482  * @param {Object} recordType Either an Array of field definition objects
6483  * as specified to {@link Roo.data.Record#create},
6484  * or an {@link Roo.data.Record} object
6485  * created using {@link Roo.data.Record#create}.
6486  */
6487 Roo.data.ArrayReader = function(meta, recordType){
6488     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6489 };
6490
6491 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6492     /**
6493      * Create a data block containing Roo.data.Records from an XML document.
6494      * @param {Object} o An Array of row objects which represents the dataset.
6495      * @return {Object} data A data block which is used by an Roo.data.Store object as
6496      * a cache of Roo.data.Records.
6497      */
6498     readRecords : function(o){
6499         var sid = this.meta ? this.meta.id : null;
6500         var recordType = this.recordType, fields = recordType.prototype.fields;
6501         var records = [];
6502         var root = o;
6503             for(var i = 0; i < root.length; i++){
6504                     var n = root[i];
6505                 var values = {};
6506                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6507                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6508                 var f = fields.items[j];
6509                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6510                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6511                 v = f.convert(v);
6512                 values[f.name] = v;
6513             }
6514                 var record = new recordType(values, id);
6515                 record.json = n;
6516                 records[records.length] = record;
6517             }
6518             return {
6519                 records : records,
6520                 totalRecords : records.length
6521             };
6522     }
6523 });/*
6524  * Based on:
6525  * Ext JS Library 1.1.1
6526  * Copyright(c) 2006-2007, Ext JS, LLC.
6527  *
6528  * Originally Released Under LGPL - original licence link has changed is not relivant.
6529  *
6530  * Fork - LGPL
6531  * <script type="text/javascript">
6532  */
6533
6534
6535 /**
6536  * @class Roo.data.Tree
6537  * @extends Roo.util.Observable
6538  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6539  * in the tree have most standard DOM functionality.
6540  * @constructor
6541  * @param {Node} root (optional) The root node
6542  */
6543 Roo.data.Tree = function(root){
6544    this.nodeHash = {};
6545    /**
6546     * The root node for this tree
6547     * @type Node
6548     */
6549    this.root = null;
6550    if(root){
6551        this.setRootNode(root);
6552    }
6553    this.addEvents({
6554        /**
6555         * @event append
6556         * Fires when a new child node is appended to a node in this tree.
6557         * @param {Tree} tree The owner tree
6558         * @param {Node} parent The parent node
6559         * @param {Node} node The newly appended node
6560         * @param {Number} index The index of the newly appended node
6561         */
6562        "append" : true,
6563        /**
6564         * @event remove
6565         * Fires when a child node is removed from a node in this tree.
6566         * @param {Tree} tree The owner tree
6567         * @param {Node} parent The parent node
6568         * @param {Node} node The child node removed
6569         */
6570        "remove" : true,
6571        /**
6572         * @event move
6573         * Fires when a node is moved to a new location in the tree
6574         * @param {Tree} tree The owner tree
6575         * @param {Node} node The node moved
6576         * @param {Node} oldParent The old parent of this node
6577         * @param {Node} newParent The new parent of this node
6578         * @param {Number} index The index it was moved to
6579         */
6580        "move" : true,
6581        /**
6582         * @event insert
6583         * Fires when a new child node is inserted in a node in this tree.
6584         * @param {Tree} tree The owner tree
6585         * @param {Node} parent The parent node
6586         * @param {Node} node The child node inserted
6587         * @param {Node} refNode The child node the node was inserted before
6588         */
6589        "insert" : true,
6590        /**
6591         * @event beforeappend
6592         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6593         * @param {Tree} tree The owner tree
6594         * @param {Node} parent The parent node
6595         * @param {Node} node The child node to be appended
6596         */
6597        "beforeappend" : true,
6598        /**
6599         * @event beforeremove
6600         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6601         * @param {Tree} tree The owner tree
6602         * @param {Node} parent The parent node
6603         * @param {Node} node The child node to be removed
6604         */
6605        "beforeremove" : true,
6606        /**
6607         * @event beforemove
6608         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6609         * @param {Tree} tree The owner tree
6610         * @param {Node} node The node being moved
6611         * @param {Node} oldParent The parent of the node
6612         * @param {Node} newParent The new parent the node is moving to
6613         * @param {Number} index The index it is being moved to
6614         */
6615        "beforemove" : true,
6616        /**
6617         * @event beforeinsert
6618         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6619         * @param {Tree} tree The owner tree
6620         * @param {Node} parent The parent node
6621         * @param {Node} node The child node to be inserted
6622         * @param {Node} refNode The child node the node is being inserted before
6623         */
6624        "beforeinsert" : true
6625    });
6626
6627     Roo.data.Tree.superclass.constructor.call(this);
6628 };
6629
6630 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6631     pathSeparator: "/",
6632
6633     proxyNodeEvent : function(){
6634         return this.fireEvent.apply(this, arguments);
6635     },
6636
6637     /**
6638      * Returns the root node for this tree.
6639      * @return {Node}
6640      */
6641     getRootNode : function(){
6642         return this.root;
6643     },
6644
6645     /**
6646      * Sets the root node for this tree.
6647      * @param {Node} node
6648      * @return {Node}
6649      */
6650     setRootNode : function(node){
6651         this.root = node;
6652         node.ownerTree = this;
6653         node.isRoot = true;
6654         this.registerNode(node);
6655         return node;
6656     },
6657
6658     /**
6659      * Gets a node in this tree by its id.
6660      * @param {String} id
6661      * @return {Node}
6662      */
6663     getNodeById : function(id){
6664         return this.nodeHash[id];
6665     },
6666
6667     registerNode : function(node){
6668         this.nodeHash[node.id] = node;
6669     },
6670
6671     unregisterNode : function(node){
6672         delete this.nodeHash[node.id];
6673     },
6674
6675     toString : function(){
6676         return "[Tree"+(this.id?" "+this.id:"")+"]";
6677     }
6678 });
6679
6680 /**
6681  * @class Roo.data.Node
6682  * @extends Roo.util.Observable
6683  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6684  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6685  * @constructor
6686  * @param {Object} attributes The attributes/config for the node
6687  */
6688 Roo.data.Node = function(attributes){
6689     /**
6690      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6691      * @type {Object}
6692      */
6693     this.attributes = attributes || {};
6694     this.leaf = this.attributes.leaf;
6695     /**
6696      * The node id. @type String
6697      */
6698     this.id = this.attributes.id;
6699     if(!this.id){
6700         this.id = Roo.id(null, "ynode-");
6701         this.attributes.id = this.id;
6702     }
6703      
6704     
6705     /**
6706      * All child nodes of this node. @type Array
6707      */
6708     this.childNodes = [];
6709     if(!this.childNodes.indexOf){ // indexOf is a must
6710         this.childNodes.indexOf = function(o){
6711             for(var i = 0, len = this.length; i < len; i++){
6712                 if(this[i] == o) {
6713                     return i;
6714                 }
6715             }
6716             return -1;
6717         };
6718     }
6719     /**
6720      * The parent node for this node. @type Node
6721      */
6722     this.parentNode = null;
6723     /**
6724      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6725      */
6726     this.firstChild = null;
6727     /**
6728      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6729      */
6730     this.lastChild = null;
6731     /**
6732      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6733      */
6734     this.previousSibling = null;
6735     /**
6736      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6737      */
6738     this.nextSibling = null;
6739
6740     this.addEvents({
6741        /**
6742         * @event append
6743         * Fires when a new child node is appended
6744         * @param {Tree} tree The owner tree
6745         * @param {Node} this This node
6746         * @param {Node} node The newly appended node
6747         * @param {Number} index The index of the newly appended node
6748         */
6749        "append" : true,
6750        /**
6751         * @event remove
6752         * Fires when a child node is removed
6753         * @param {Tree} tree The owner tree
6754         * @param {Node} this This node
6755         * @param {Node} node The removed node
6756         */
6757        "remove" : true,
6758        /**
6759         * @event move
6760         * Fires when this node is moved to a new location in the tree
6761         * @param {Tree} tree The owner tree
6762         * @param {Node} this This node
6763         * @param {Node} oldParent The old parent of this node
6764         * @param {Node} newParent The new parent of this node
6765         * @param {Number} index The index it was moved to
6766         */
6767        "move" : true,
6768        /**
6769         * @event insert
6770         * Fires when a new child node is inserted.
6771         * @param {Tree} tree The owner tree
6772         * @param {Node} this This node
6773         * @param {Node} node The child node inserted
6774         * @param {Node} refNode The child node the node was inserted before
6775         */
6776        "insert" : true,
6777        /**
6778         * @event beforeappend
6779         * Fires before a new child is appended, return false to cancel the append.
6780         * @param {Tree} tree The owner tree
6781         * @param {Node} this This node
6782         * @param {Node} node The child node to be appended
6783         */
6784        "beforeappend" : true,
6785        /**
6786         * @event beforeremove
6787         * Fires before a child is removed, return false to cancel the remove.
6788         * @param {Tree} tree The owner tree
6789         * @param {Node} this This node
6790         * @param {Node} node The child node to be removed
6791         */
6792        "beforeremove" : true,
6793        /**
6794         * @event beforemove
6795         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6796         * @param {Tree} tree The owner tree
6797         * @param {Node} this This node
6798         * @param {Node} oldParent The parent of this node
6799         * @param {Node} newParent The new parent this node is moving to
6800         * @param {Number} index The index it is being moved to
6801         */
6802        "beforemove" : true,
6803        /**
6804         * @event beforeinsert
6805         * Fires before a new child is inserted, return false to cancel the insert.
6806         * @param {Tree} tree The owner tree
6807         * @param {Node} this This node
6808         * @param {Node} node The child node to be inserted
6809         * @param {Node} refNode The child node the node is being inserted before
6810         */
6811        "beforeinsert" : true
6812    });
6813     this.listeners = this.attributes.listeners;
6814     Roo.data.Node.superclass.constructor.call(this);
6815 };
6816
6817 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6818     fireEvent : function(evtName){
6819         // first do standard event for this node
6820         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6821             return false;
6822         }
6823         // then bubble it up to the tree if the event wasn't cancelled
6824         var ot = this.getOwnerTree();
6825         if(ot){
6826             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6827                 return false;
6828             }
6829         }
6830         return true;
6831     },
6832
6833     /**
6834      * Returns true if this node is a leaf
6835      * @return {Boolean}
6836      */
6837     isLeaf : function(){
6838         return this.leaf === true;
6839     },
6840
6841     // private
6842     setFirstChild : function(node){
6843         this.firstChild = node;
6844     },
6845
6846     //private
6847     setLastChild : function(node){
6848         this.lastChild = node;
6849     },
6850
6851
6852     /**
6853      * Returns true if this node is the last child of its parent
6854      * @return {Boolean}
6855      */
6856     isLast : function(){
6857        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6858     },
6859
6860     /**
6861      * Returns true if this node is the first child of its parent
6862      * @return {Boolean}
6863      */
6864     isFirst : function(){
6865        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6866     },
6867
6868     hasChildNodes : function(){
6869         return !this.isLeaf() && this.childNodes.length > 0;
6870     },
6871
6872     /**
6873      * Insert node(s) as the last child node of this node.
6874      * @param {Node/Array} node The node or Array of nodes to append
6875      * @return {Node} The appended node if single append, or null if an array was passed
6876      */
6877     appendChild : function(node){
6878         var multi = false;
6879         if(node instanceof Array){
6880             multi = node;
6881         }else if(arguments.length > 1){
6882             multi = arguments;
6883         }
6884         // if passed an array or multiple args do them one by one
6885         if(multi){
6886             for(var i = 0, len = multi.length; i < len; i++) {
6887                 this.appendChild(multi[i]);
6888             }
6889         }else{
6890             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6891                 return false;
6892             }
6893             var index = this.childNodes.length;
6894             var oldParent = node.parentNode;
6895             // it's a move, make sure we move it cleanly
6896             if(oldParent){
6897                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6898                     return false;
6899                 }
6900                 oldParent.removeChild(node);
6901             }
6902             index = this.childNodes.length;
6903             if(index == 0){
6904                 this.setFirstChild(node);
6905             }
6906             this.childNodes.push(node);
6907             node.parentNode = this;
6908             var ps = this.childNodes[index-1];
6909             if(ps){
6910                 node.previousSibling = ps;
6911                 ps.nextSibling = node;
6912             }else{
6913                 node.previousSibling = null;
6914             }
6915             node.nextSibling = null;
6916             this.setLastChild(node);
6917             node.setOwnerTree(this.getOwnerTree());
6918             this.fireEvent("append", this.ownerTree, this, node, index);
6919             if(oldParent){
6920                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6921             }
6922             return node;
6923         }
6924     },
6925
6926     /**
6927      * Removes a child node from this node.
6928      * @param {Node} node The node to remove
6929      * @return {Node} The removed node
6930      */
6931     removeChild : function(node){
6932         var index = this.childNodes.indexOf(node);
6933         if(index == -1){
6934             return false;
6935         }
6936         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6937             return false;
6938         }
6939
6940         // remove it from childNodes collection
6941         this.childNodes.splice(index, 1);
6942
6943         // update siblings
6944         if(node.previousSibling){
6945             node.previousSibling.nextSibling = node.nextSibling;
6946         }
6947         if(node.nextSibling){
6948             node.nextSibling.previousSibling = node.previousSibling;
6949         }
6950
6951         // update child refs
6952         if(this.firstChild == node){
6953             this.setFirstChild(node.nextSibling);
6954         }
6955         if(this.lastChild == node){
6956             this.setLastChild(node.previousSibling);
6957         }
6958
6959         node.setOwnerTree(null);
6960         // clear any references from the node
6961         node.parentNode = null;
6962         node.previousSibling = null;
6963         node.nextSibling = null;
6964         this.fireEvent("remove", this.ownerTree, this, node);
6965         return node;
6966     },
6967
6968     /**
6969      * Inserts the first node before the second node in this nodes childNodes collection.
6970      * @param {Node} node The node to insert
6971      * @param {Node} refNode The node to insert before (if null the node is appended)
6972      * @return {Node} The inserted node
6973      */
6974     insertBefore : function(node, refNode){
6975         if(!refNode){ // like standard Dom, refNode can be null for append
6976             return this.appendChild(node);
6977         }
6978         // nothing to do
6979         if(node == refNode){
6980             return false;
6981         }
6982
6983         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6984             return false;
6985         }
6986         var index = this.childNodes.indexOf(refNode);
6987         var oldParent = node.parentNode;
6988         var refIndex = index;
6989
6990         // when moving internally, indexes will change after remove
6991         if(oldParent == this && this.childNodes.indexOf(node) < index){
6992             refIndex--;
6993         }
6994
6995         // it's a move, make sure we move it cleanly
6996         if(oldParent){
6997             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6998                 return false;
6999             }
7000             oldParent.removeChild(node);
7001         }
7002         if(refIndex == 0){
7003             this.setFirstChild(node);
7004         }
7005         this.childNodes.splice(refIndex, 0, node);
7006         node.parentNode = this;
7007         var ps = this.childNodes[refIndex-1];
7008         if(ps){
7009             node.previousSibling = ps;
7010             ps.nextSibling = node;
7011         }else{
7012             node.previousSibling = null;
7013         }
7014         node.nextSibling = refNode;
7015         refNode.previousSibling = node;
7016         node.setOwnerTree(this.getOwnerTree());
7017         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7018         if(oldParent){
7019             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7020         }
7021         return node;
7022     },
7023
7024     /**
7025      * Returns the child node at the specified index.
7026      * @param {Number} index
7027      * @return {Node}
7028      */
7029     item : function(index){
7030         return this.childNodes[index];
7031     },
7032
7033     /**
7034      * Replaces one child node in this node with another.
7035      * @param {Node} newChild The replacement node
7036      * @param {Node} oldChild The node to replace
7037      * @return {Node} The replaced node
7038      */
7039     replaceChild : function(newChild, oldChild){
7040         this.insertBefore(newChild, oldChild);
7041         this.removeChild(oldChild);
7042         return oldChild;
7043     },
7044
7045     /**
7046      * Returns the index of a child node
7047      * @param {Node} node
7048      * @return {Number} The index of the node or -1 if it was not found
7049      */
7050     indexOf : function(child){
7051         return this.childNodes.indexOf(child);
7052     },
7053
7054     /**
7055      * Returns the tree this node is in.
7056      * @return {Tree}
7057      */
7058     getOwnerTree : function(){
7059         // if it doesn't have one, look for one
7060         if(!this.ownerTree){
7061             var p = this;
7062             while(p){
7063                 if(p.ownerTree){
7064                     this.ownerTree = p.ownerTree;
7065                     break;
7066                 }
7067                 p = p.parentNode;
7068             }
7069         }
7070         return this.ownerTree;
7071     },
7072
7073     /**
7074      * Returns depth of this node (the root node has a depth of 0)
7075      * @return {Number}
7076      */
7077     getDepth : function(){
7078         var depth = 0;
7079         var p = this;
7080         while(p.parentNode){
7081             ++depth;
7082             p = p.parentNode;
7083         }
7084         return depth;
7085     },
7086
7087     // private
7088     setOwnerTree : function(tree){
7089         // if it's move, we need to update everyone
7090         if(tree != this.ownerTree){
7091             if(this.ownerTree){
7092                 this.ownerTree.unregisterNode(this);
7093             }
7094             this.ownerTree = tree;
7095             var cs = this.childNodes;
7096             for(var i = 0, len = cs.length; i < len; i++) {
7097                 cs[i].setOwnerTree(tree);
7098             }
7099             if(tree){
7100                 tree.registerNode(this);
7101             }
7102         }
7103     },
7104
7105     /**
7106      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7107      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7108      * @return {String} The path
7109      */
7110     getPath : function(attr){
7111         attr = attr || "id";
7112         var p = this.parentNode;
7113         var b = [this.attributes[attr]];
7114         while(p){
7115             b.unshift(p.attributes[attr]);
7116             p = p.parentNode;
7117         }
7118         var sep = this.getOwnerTree().pathSeparator;
7119         return sep + b.join(sep);
7120     },
7121
7122     /**
7123      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7124      * function call will be the scope provided or the current node. The arguments to the function
7125      * will be the args provided or the current node. If the function returns false at any point,
7126      * the bubble is stopped.
7127      * @param {Function} fn The function to call
7128      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7129      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7130      */
7131     bubble : function(fn, scope, args){
7132         var p = this;
7133         while(p){
7134             if(fn.call(scope || p, args || p) === false){
7135                 break;
7136             }
7137             p = p.parentNode;
7138         }
7139     },
7140
7141     /**
7142      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7143      * function call will be the scope provided or the current node. The arguments to the function
7144      * will be the args provided or the current node. If the function returns false at any point,
7145      * the cascade is stopped on that branch.
7146      * @param {Function} fn The function to call
7147      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7148      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7149      */
7150     cascade : function(fn, scope, args){
7151         if(fn.call(scope || this, args || this) !== false){
7152             var cs = this.childNodes;
7153             for(var i = 0, len = cs.length; i < len; i++) {
7154                 cs[i].cascade(fn, scope, args);
7155             }
7156         }
7157     },
7158
7159     /**
7160      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7161      * function call will be the scope provided or the current node. The arguments to the function
7162      * will be the args provided or the current node. If the function returns false at any point,
7163      * the iteration stops.
7164      * @param {Function} fn The function to call
7165      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7166      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7167      */
7168     eachChild : function(fn, scope, args){
7169         var cs = this.childNodes;
7170         for(var i = 0, len = cs.length; i < len; i++) {
7171                 if(fn.call(scope || this, args || cs[i]) === false){
7172                     break;
7173                 }
7174         }
7175     },
7176
7177     /**
7178      * Finds the first child that has the attribute with the specified value.
7179      * @param {String} attribute The attribute name
7180      * @param {Mixed} value The value to search for
7181      * @return {Node} The found child or null if none was found
7182      */
7183     findChild : function(attribute, value){
7184         var cs = this.childNodes;
7185         for(var i = 0, len = cs.length; i < len; i++) {
7186                 if(cs[i].attributes[attribute] == value){
7187                     return cs[i];
7188                 }
7189         }
7190         return null;
7191     },
7192
7193     /**
7194      * Finds the first child by a custom function. The child matches if the function passed
7195      * returns true.
7196      * @param {Function} fn
7197      * @param {Object} scope (optional)
7198      * @return {Node} The found child or null if none was found
7199      */
7200     findChildBy : function(fn, scope){
7201         var cs = this.childNodes;
7202         for(var i = 0, len = cs.length; i < len; i++) {
7203                 if(fn.call(scope||cs[i], cs[i]) === true){
7204                     return cs[i];
7205                 }
7206         }
7207         return null;
7208     },
7209
7210     /**
7211      * Sorts this nodes children using the supplied sort function
7212      * @param {Function} fn
7213      * @param {Object} scope (optional)
7214      */
7215     sort : function(fn, scope){
7216         var cs = this.childNodes;
7217         var len = cs.length;
7218         if(len > 0){
7219             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7220             cs.sort(sortFn);
7221             for(var i = 0; i < len; i++){
7222                 var n = cs[i];
7223                 n.previousSibling = cs[i-1];
7224                 n.nextSibling = cs[i+1];
7225                 if(i == 0){
7226                     this.setFirstChild(n);
7227                 }
7228                 if(i == len-1){
7229                     this.setLastChild(n);
7230                 }
7231             }
7232         }
7233     },
7234
7235     /**
7236      * Returns true if this node is an ancestor (at any point) of the passed node.
7237      * @param {Node} node
7238      * @return {Boolean}
7239      */
7240     contains : function(node){
7241         return node.isAncestor(this);
7242     },
7243
7244     /**
7245      * Returns true if the passed node is an ancestor (at any point) of this node.
7246      * @param {Node} node
7247      * @return {Boolean}
7248      */
7249     isAncestor : function(node){
7250         var p = this.parentNode;
7251         while(p){
7252             if(p == node){
7253                 return true;
7254             }
7255             p = p.parentNode;
7256         }
7257         return false;
7258     },
7259
7260     toString : function(){
7261         return "[Node"+(this.id?" "+this.id:"")+"]";
7262     }
7263 });/*
7264  * Based on:
7265  * Ext JS Library 1.1.1
7266  * Copyright(c) 2006-2007, Ext JS, LLC.
7267  *
7268  * Originally Released Under LGPL - original licence link has changed is not relivant.
7269  *
7270  * Fork - LGPL
7271  * <script type="text/javascript">
7272  */
7273  
7274
7275 /**
7276  * @class Roo.ComponentMgr
7277  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7278  * @singleton
7279  */
7280 Roo.ComponentMgr = function(){
7281     var all = new Roo.util.MixedCollection();
7282
7283     return {
7284         /**
7285          * Registers a component.
7286          * @param {Roo.Component} c The component
7287          */
7288         register : function(c){
7289             all.add(c);
7290         },
7291
7292         /**
7293          * Unregisters a component.
7294          * @param {Roo.Component} c The component
7295          */
7296         unregister : function(c){
7297             all.remove(c);
7298         },
7299
7300         /**
7301          * Returns a component by id
7302          * @param {String} id The component id
7303          */
7304         get : function(id){
7305             return all.get(id);
7306         },
7307
7308         /**
7309          * Registers a function that will be called when a specified component is added to ComponentMgr
7310          * @param {String} id The component id
7311          * @param {Funtction} fn The callback function
7312          * @param {Object} scope The scope of the callback
7313          */
7314         onAvailable : function(id, fn, scope){
7315             all.on("add", function(index, o){
7316                 if(o.id == id){
7317                     fn.call(scope || o, o);
7318                     all.un("add", fn, scope);
7319                 }
7320             });
7321         }
7322     };
7323 }();/*
7324  * Based on:
7325  * Ext JS Library 1.1.1
7326  * Copyright(c) 2006-2007, Ext JS, LLC.
7327  *
7328  * Originally Released Under LGPL - original licence link has changed is not relivant.
7329  *
7330  * Fork - LGPL
7331  * <script type="text/javascript">
7332  */
7333  
7334 /**
7335  * @class Roo.Component
7336  * @extends Roo.util.Observable
7337  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7338  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7339  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7340  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7341  * All visual components (widgets) that require rendering into a layout should subclass Component.
7342  * @constructor
7343  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7344  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
7345  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7346  */
7347 Roo.Component = function(config){
7348     config = config || {};
7349     if(config.tagName || config.dom || typeof config == "string"){ // element object
7350         config = {el: config, id: config.id || config};
7351     }
7352     this.initialConfig = config;
7353
7354     Roo.apply(this, config);
7355     this.addEvents({
7356         /**
7357          * @event disable
7358          * Fires after the component is disabled.
7359              * @param {Roo.Component} this
7360              */
7361         disable : true,
7362         /**
7363          * @event enable
7364          * Fires after the component is enabled.
7365              * @param {Roo.Component} this
7366              */
7367         enable : true,
7368         /**
7369          * @event beforeshow
7370          * Fires before the component is shown.  Return false to stop the show.
7371              * @param {Roo.Component} this
7372              */
7373         beforeshow : true,
7374         /**
7375          * @event show
7376          * Fires after the component is shown.
7377              * @param {Roo.Component} this
7378              */
7379         show : true,
7380         /**
7381          * @event beforehide
7382          * Fires before the component is hidden. Return false to stop the hide.
7383              * @param {Roo.Component} this
7384              */
7385         beforehide : true,
7386         /**
7387          * @event hide
7388          * Fires after the component is hidden.
7389              * @param {Roo.Component} this
7390              */
7391         hide : true,
7392         /**
7393          * @event beforerender
7394          * Fires before the component is rendered. Return false to stop the render.
7395              * @param {Roo.Component} this
7396              */
7397         beforerender : true,
7398         /**
7399          * @event render
7400          * Fires after the component is rendered.
7401              * @param {Roo.Component} this
7402              */
7403         render : true,
7404         /**
7405          * @event beforedestroy
7406          * Fires before the component is destroyed. Return false to stop the destroy.
7407              * @param {Roo.Component} this
7408              */
7409         beforedestroy : true,
7410         /**
7411          * @event destroy
7412          * Fires after the component is destroyed.
7413              * @param {Roo.Component} this
7414              */
7415         destroy : true
7416     });
7417     if(!this.id){
7418         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7419     }
7420     Roo.ComponentMgr.register(this);
7421     Roo.Component.superclass.constructor.call(this);
7422     this.initComponent();
7423     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7424         this.render(this.renderTo);
7425         delete this.renderTo;
7426     }
7427 };
7428
7429 /** @private */
7430 Roo.Component.AUTO_ID = 1000;
7431
7432 Roo.extend(Roo.Component, Roo.util.Observable, {
7433     /**
7434      * @scope Roo.Component.prototype
7435      * @type {Boolean}
7436      * true if this component is hidden. Read-only.
7437      */
7438     hidden : false,
7439     /**
7440      * @type {Boolean}
7441      * true if this component is disabled. Read-only.
7442      */
7443     disabled : false,
7444     /**
7445      * @type {Boolean}
7446      * true if this component has been rendered. Read-only.
7447      */
7448     rendered : false,
7449     
7450     /** @cfg {String} disableClass
7451      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7452      */
7453     disabledClass : "x-item-disabled",
7454         /** @cfg {Boolean} allowDomMove
7455          * Whether the component can move the Dom node when rendering (defaults to true).
7456          */
7457     allowDomMove : true,
7458     /** @cfg {String} hideMode
7459      * How this component should hidden. Supported values are
7460      * "visibility" (css visibility), "offsets" (negative offset position) and
7461      * "display" (css display) - defaults to "display".
7462      */
7463     hideMode: 'display',
7464
7465     /** @private */
7466     ctype : "Roo.Component",
7467
7468     /**
7469      * @cfg {String} actionMode 
7470      * which property holds the element that used for  hide() / show() / disable() / enable()
7471      * default is 'el' 
7472      */
7473     actionMode : "el",
7474
7475     /** @private */
7476     getActionEl : function(){
7477         return this[this.actionMode];
7478     },
7479
7480     initComponent : Roo.emptyFn,
7481     /**
7482      * If this is a lazy rendering component, render it to its container element.
7483      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
7484      */
7485     render : function(container, position){
7486         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7487             if(!container && this.el){
7488                 this.el = Roo.get(this.el);
7489                 container = this.el.dom.parentNode;
7490                 this.allowDomMove = false;
7491             }
7492             this.container = Roo.get(container);
7493             this.rendered = true;
7494             if(position !== undefined){
7495                 if(typeof position == 'number'){
7496                     position = this.container.dom.childNodes[position];
7497                 }else{
7498                     position = Roo.getDom(position);
7499                 }
7500             }
7501             this.onRender(this.container, position || null);
7502             if(this.cls){
7503                 this.el.addClass(this.cls);
7504                 delete this.cls;
7505             }
7506             if(this.style){
7507                 this.el.applyStyles(this.style);
7508                 delete this.style;
7509             }
7510             this.fireEvent("render", this);
7511             this.afterRender(this.container);
7512             if(this.hidden){
7513                 this.hide();
7514             }
7515             if(this.disabled){
7516                 this.disable();
7517             }
7518         }
7519         return this;
7520     },
7521
7522     /** @private */
7523     // default function is not really useful
7524     onRender : function(ct, position){
7525         if(this.el){
7526             this.el = Roo.get(this.el);
7527             if(this.allowDomMove !== false){
7528                 ct.dom.insertBefore(this.el.dom, position);
7529             }
7530         }
7531     },
7532
7533     /** @private */
7534     getAutoCreate : function(){
7535         var cfg = typeof this.autoCreate == "object" ?
7536                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7537         if(this.id && !cfg.id){
7538             cfg.id = this.id;
7539         }
7540         return cfg;
7541     },
7542
7543     /** @private */
7544     afterRender : Roo.emptyFn,
7545
7546     /**
7547      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7548      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7549      */
7550     destroy : function(){
7551         if(this.fireEvent("beforedestroy", this) !== false){
7552             this.purgeListeners();
7553             this.beforeDestroy();
7554             if(this.rendered){
7555                 this.el.removeAllListeners();
7556                 this.el.remove();
7557                 if(this.actionMode == "container"){
7558                     this.container.remove();
7559                 }
7560             }
7561             this.onDestroy();
7562             Roo.ComponentMgr.unregister(this);
7563             this.fireEvent("destroy", this);
7564         }
7565     },
7566
7567         /** @private */
7568     beforeDestroy : function(){
7569
7570     },
7571
7572         /** @private */
7573         onDestroy : function(){
7574
7575     },
7576
7577     /**
7578      * Returns the underlying {@link Roo.Element}.
7579      * @return {Roo.Element} The element
7580      */
7581     getEl : function(){
7582         return this.el;
7583     },
7584
7585     /**
7586      * Returns the id of this component.
7587      * @return {String}
7588      */
7589     getId : function(){
7590         return this.id;
7591     },
7592
7593     /**
7594      * Try to focus this component.
7595      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7596      * @return {Roo.Component} this
7597      */
7598     focus : function(selectText){
7599         if(this.rendered){
7600             this.el.focus();
7601             if(selectText === true){
7602                 this.el.dom.select();
7603             }
7604         }
7605         return this;
7606     },
7607
7608     /** @private */
7609     blur : function(){
7610         if(this.rendered){
7611             this.el.blur();
7612         }
7613         return this;
7614     },
7615
7616     /**
7617      * Disable this component.
7618      * @return {Roo.Component} this
7619      */
7620     disable : function(){
7621         if(this.rendered){
7622             this.onDisable();
7623         }
7624         this.disabled = true;
7625         this.fireEvent("disable", this);
7626         return this;
7627     },
7628
7629         // private
7630     onDisable : function(){
7631         this.getActionEl().addClass(this.disabledClass);
7632         this.el.dom.disabled = true;
7633     },
7634
7635     /**
7636      * Enable this component.
7637      * @return {Roo.Component} this
7638      */
7639     enable : function(){
7640         if(this.rendered){
7641             this.onEnable();
7642         }
7643         this.disabled = false;
7644         this.fireEvent("enable", this);
7645         return this;
7646     },
7647
7648         // private
7649     onEnable : function(){
7650         this.getActionEl().removeClass(this.disabledClass);
7651         this.el.dom.disabled = false;
7652     },
7653
7654     /**
7655      * Convenience function for setting disabled/enabled by boolean.
7656      * @param {Boolean} disabled
7657      */
7658     setDisabled : function(disabled){
7659         this[disabled ? "disable" : "enable"]();
7660     },
7661
7662     /**
7663      * Show this component.
7664      * @return {Roo.Component} this
7665      */
7666     show: function(){
7667         if(this.fireEvent("beforeshow", this) !== false){
7668             this.hidden = false;
7669             if(this.rendered){
7670                 this.onShow();
7671             }
7672             this.fireEvent("show", this);
7673         }
7674         return this;
7675     },
7676
7677     // private
7678     onShow : function(){
7679         var ae = this.getActionEl();
7680         if(this.hideMode == 'visibility'){
7681             ae.dom.style.visibility = "visible";
7682         }else if(this.hideMode == 'offsets'){
7683             ae.removeClass('x-hidden');
7684         }else{
7685             ae.dom.style.display = "";
7686         }
7687     },
7688
7689     /**
7690      * Hide this component.
7691      * @return {Roo.Component} this
7692      */
7693     hide: function(){
7694         if(this.fireEvent("beforehide", this) !== false){
7695             this.hidden = true;
7696             if(this.rendered){
7697                 this.onHide();
7698             }
7699             this.fireEvent("hide", this);
7700         }
7701         return this;
7702     },
7703
7704     // private
7705     onHide : function(){
7706         var ae = this.getActionEl();
7707         if(this.hideMode == 'visibility'){
7708             ae.dom.style.visibility = "hidden";
7709         }else if(this.hideMode == 'offsets'){
7710             ae.addClass('x-hidden');
7711         }else{
7712             ae.dom.style.display = "none";
7713         }
7714     },
7715
7716     /**
7717      * Convenience function to hide or show this component by boolean.
7718      * @param {Boolean} visible True to show, false to hide
7719      * @return {Roo.Component} this
7720      */
7721     setVisible: function(visible){
7722         if(visible) {
7723             this.show();
7724         }else{
7725             this.hide();
7726         }
7727         return this;
7728     },
7729
7730     /**
7731      * Returns true if this component is visible.
7732      */
7733     isVisible : function(){
7734         return this.getActionEl().isVisible();
7735     },
7736
7737     cloneConfig : function(overrides){
7738         overrides = overrides || {};
7739         var id = overrides.id || Roo.id();
7740         var cfg = Roo.applyIf(overrides, this.initialConfig);
7741         cfg.id = id; // prevent dup id
7742         return new this.constructor(cfg);
7743     }
7744 });/*
7745  * Based on:
7746  * Ext JS Library 1.1.1
7747  * Copyright(c) 2006-2007, Ext JS, LLC.
7748  *
7749  * Originally Released Under LGPL - original licence link has changed is not relivant.
7750  *
7751  * Fork - LGPL
7752  * <script type="text/javascript">
7753  */
7754  (function(){ 
7755 /**
7756  * @class Roo.Layer
7757  * @extends Roo.Element
7758  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7759  * automatic maintaining of shadow/shim positions.
7760  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7761  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7762  * you can pass a string with a CSS class name. False turns off the shadow.
7763  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7764  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7765  * @cfg {String} cls CSS class to add to the element
7766  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7767  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7768  * @constructor
7769  * @param {Object} config An object with config options.
7770  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7771  */
7772
7773 Roo.Layer = function(config, existingEl){
7774     config = config || {};
7775     var dh = Roo.DomHelper;
7776     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7777     if(existingEl){
7778         this.dom = Roo.getDom(existingEl);
7779     }
7780     if(!this.dom){
7781         var o = config.dh || {tag: "div", cls: "x-layer"};
7782         this.dom = dh.append(pel, o);
7783     }
7784     if(config.cls){
7785         this.addClass(config.cls);
7786     }
7787     this.constrain = config.constrain !== false;
7788     this.visibilityMode = Roo.Element.VISIBILITY;
7789     if(config.id){
7790         this.id = this.dom.id = config.id;
7791     }else{
7792         this.id = Roo.id(this.dom);
7793     }
7794     this.zindex = config.zindex || this.getZIndex();
7795     this.position("absolute", this.zindex);
7796     if(config.shadow){
7797         this.shadowOffset = config.shadowOffset || 4;
7798         this.shadow = new Roo.Shadow({
7799             offset : this.shadowOffset,
7800             mode : config.shadow
7801         });
7802     }else{
7803         this.shadowOffset = 0;
7804     }
7805     this.useShim = config.shim !== false && Roo.useShims;
7806     this.useDisplay = config.useDisplay;
7807     this.hide();
7808 };
7809
7810 var supr = Roo.Element.prototype;
7811
7812 // shims are shared among layer to keep from having 100 iframes
7813 var shims = [];
7814
7815 Roo.extend(Roo.Layer, Roo.Element, {
7816
7817     getZIndex : function(){
7818         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7819     },
7820
7821     getShim : function(){
7822         if(!this.useShim){
7823             return null;
7824         }
7825         if(this.shim){
7826             return this.shim;
7827         }
7828         var shim = shims.shift();
7829         if(!shim){
7830             shim = this.createShim();
7831             shim.enableDisplayMode('block');
7832             shim.dom.style.display = 'none';
7833             shim.dom.style.visibility = 'visible';
7834         }
7835         var pn = this.dom.parentNode;
7836         if(shim.dom.parentNode != pn){
7837             pn.insertBefore(shim.dom, this.dom);
7838         }
7839         shim.setStyle('z-index', this.getZIndex()-2);
7840         this.shim = shim;
7841         return shim;
7842     },
7843
7844     hideShim : function(){
7845         if(this.shim){
7846             this.shim.setDisplayed(false);
7847             shims.push(this.shim);
7848             delete this.shim;
7849         }
7850     },
7851
7852     disableShadow : function(){
7853         if(this.shadow){
7854             this.shadowDisabled = true;
7855             this.shadow.hide();
7856             this.lastShadowOffset = this.shadowOffset;
7857             this.shadowOffset = 0;
7858         }
7859     },
7860
7861     enableShadow : function(show){
7862         if(this.shadow){
7863             this.shadowDisabled = false;
7864             this.shadowOffset = this.lastShadowOffset;
7865             delete this.lastShadowOffset;
7866             if(show){
7867                 this.sync(true);
7868             }
7869         }
7870     },
7871
7872     // private
7873     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7874     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7875     sync : function(doShow){
7876         var sw = this.shadow;
7877         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7878             var sh = this.getShim();
7879
7880             var w = this.getWidth(),
7881                 h = this.getHeight();
7882
7883             var l = this.getLeft(true),
7884                 t = this.getTop(true);
7885
7886             if(sw && !this.shadowDisabled){
7887                 if(doShow && !sw.isVisible()){
7888                     sw.show(this);
7889                 }else{
7890                     sw.realign(l, t, w, h);
7891                 }
7892                 if(sh){
7893                     if(doShow){
7894                        sh.show();
7895                     }
7896                     // fit the shim behind the shadow, so it is shimmed too
7897                     var a = sw.adjusts, s = sh.dom.style;
7898                     s.left = (Math.min(l, l+a.l))+"px";
7899                     s.top = (Math.min(t, t+a.t))+"px";
7900                     s.width = (w+a.w)+"px";
7901                     s.height = (h+a.h)+"px";
7902                 }
7903             }else if(sh){
7904                 if(doShow){
7905                    sh.show();
7906                 }
7907                 sh.setSize(w, h);
7908                 sh.setLeftTop(l, t);
7909             }
7910             
7911         }
7912     },
7913
7914     // private
7915     destroy : function(){
7916         this.hideShim();
7917         if(this.shadow){
7918             this.shadow.hide();
7919         }
7920         this.removeAllListeners();
7921         var pn = this.dom.parentNode;
7922         if(pn){
7923             pn.removeChild(this.dom);
7924         }
7925         Roo.Element.uncache(this.id);
7926     },
7927
7928     remove : function(){
7929         this.destroy();
7930     },
7931
7932     // private
7933     beginUpdate : function(){
7934         this.updating = true;
7935     },
7936
7937     // private
7938     endUpdate : function(){
7939         this.updating = false;
7940         this.sync(true);
7941     },
7942
7943     // private
7944     hideUnders : function(negOffset){
7945         if(this.shadow){
7946             this.shadow.hide();
7947         }
7948         this.hideShim();
7949     },
7950
7951     // private
7952     constrainXY : function(){
7953         if(this.constrain){
7954             var vw = Roo.lib.Dom.getViewWidth(),
7955                 vh = Roo.lib.Dom.getViewHeight();
7956             var s = Roo.get(document).getScroll();
7957
7958             var xy = this.getXY();
7959             var x = xy[0], y = xy[1];   
7960             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7961             // only move it if it needs it
7962             var moved = false;
7963             // first validate right/bottom
7964             if((x + w) > vw+s.left){
7965                 x = vw - w - this.shadowOffset;
7966                 moved = true;
7967             }
7968             if((y + h) > vh+s.top){
7969                 y = vh - h - this.shadowOffset;
7970                 moved = true;
7971             }
7972             // then make sure top/left isn't negative
7973             if(x < s.left){
7974                 x = s.left;
7975                 moved = true;
7976             }
7977             if(y < s.top){
7978                 y = s.top;
7979                 moved = true;
7980             }
7981             if(moved){
7982                 if(this.avoidY){
7983                     var ay = this.avoidY;
7984                     if(y <= ay && (y+h) >= ay){
7985                         y = ay-h-5;   
7986                     }
7987                 }
7988                 xy = [x, y];
7989                 this.storeXY(xy);
7990                 supr.setXY.call(this, xy);
7991                 this.sync();
7992             }
7993         }
7994     },
7995
7996     isVisible : function(){
7997         return this.visible;    
7998     },
7999
8000     // private
8001     showAction : function(){
8002         this.visible = true; // track visibility to prevent getStyle calls
8003         if(this.useDisplay === true){
8004             this.setDisplayed("");
8005         }else if(this.lastXY){
8006             supr.setXY.call(this, this.lastXY);
8007         }else if(this.lastLT){
8008             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
8009         }
8010     },
8011
8012     // private
8013     hideAction : function(){
8014         this.visible = false;
8015         if(this.useDisplay === true){
8016             this.setDisplayed(false);
8017         }else{
8018             this.setLeftTop(-10000,-10000);
8019         }
8020     },
8021
8022     // overridden Element method
8023     setVisible : function(v, a, d, c, e){
8024         if(v){
8025             this.showAction();
8026         }
8027         if(a && v){
8028             var cb = function(){
8029                 this.sync(true);
8030                 if(c){
8031                     c();
8032                 }
8033             }.createDelegate(this);
8034             supr.setVisible.call(this, true, true, d, cb, e);
8035         }else{
8036             if(!v){
8037                 this.hideUnders(true);
8038             }
8039             var cb = c;
8040             if(a){
8041                 cb = function(){
8042                     this.hideAction();
8043                     if(c){
8044                         c();
8045                     }
8046                 }.createDelegate(this);
8047             }
8048             supr.setVisible.call(this, v, a, d, cb, e);
8049             if(v){
8050                 this.sync(true);
8051             }else if(!a){
8052                 this.hideAction();
8053             }
8054         }
8055     },
8056
8057     storeXY : function(xy){
8058         delete this.lastLT;
8059         this.lastXY = xy;
8060     },
8061
8062     storeLeftTop : function(left, top){
8063         delete this.lastXY;
8064         this.lastLT = [left, top];
8065     },
8066
8067     // private
8068     beforeFx : function(){
8069         this.beforeAction();
8070         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8071     },
8072
8073     // private
8074     afterFx : function(){
8075         Roo.Layer.superclass.afterFx.apply(this, arguments);
8076         this.sync(this.isVisible());
8077     },
8078
8079     // private
8080     beforeAction : function(){
8081         if(!this.updating && this.shadow){
8082             this.shadow.hide();
8083         }
8084     },
8085
8086     // overridden Element method
8087     setLeft : function(left){
8088         this.storeLeftTop(left, this.getTop(true));
8089         supr.setLeft.apply(this, arguments);
8090         this.sync();
8091     },
8092
8093     setTop : function(top){
8094         this.storeLeftTop(this.getLeft(true), top);
8095         supr.setTop.apply(this, arguments);
8096         this.sync();
8097     },
8098
8099     setLeftTop : function(left, top){
8100         this.storeLeftTop(left, top);
8101         supr.setLeftTop.apply(this, arguments);
8102         this.sync();
8103     },
8104
8105     setXY : function(xy, a, d, c, e){
8106         this.fixDisplay();
8107         this.beforeAction();
8108         this.storeXY(xy);
8109         var cb = this.createCB(c);
8110         supr.setXY.call(this, xy, a, d, cb, e);
8111         if(!a){
8112             cb();
8113         }
8114     },
8115
8116     // private
8117     createCB : function(c){
8118         var el = this;
8119         return function(){
8120             el.constrainXY();
8121             el.sync(true);
8122             if(c){
8123                 c();
8124             }
8125         };
8126     },
8127
8128     // overridden Element method
8129     setX : function(x, a, d, c, e){
8130         this.setXY([x, this.getY()], a, d, c, e);
8131     },
8132
8133     // overridden Element method
8134     setY : function(y, a, d, c, e){
8135         this.setXY([this.getX(), y], a, d, c, e);
8136     },
8137
8138     // overridden Element method
8139     setSize : function(w, h, a, d, c, e){
8140         this.beforeAction();
8141         var cb = this.createCB(c);
8142         supr.setSize.call(this, w, h, a, d, cb, e);
8143         if(!a){
8144             cb();
8145         }
8146     },
8147
8148     // overridden Element method
8149     setWidth : function(w, a, d, c, e){
8150         this.beforeAction();
8151         var cb = this.createCB(c);
8152         supr.setWidth.call(this, w, a, d, cb, e);
8153         if(!a){
8154             cb();
8155         }
8156     },
8157
8158     // overridden Element method
8159     setHeight : function(h, a, d, c, e){
8160         this.beforeAction();
8161         var cb = this.createCB(c);
8162         supr.setHeight.call(this, h, a, d, cb, e);
8163         if(!a){
8164             cb();
8165         }
8166     },
8167
8168     // overridden Element method
8169     setBounds : function(x, y, w, h, a, d, c, e){
8170         this.beforeAction();
8171         var cb = this.createCB(c);
8172         if(!a){
8173             this.storeXY([x, y]);
8174             supr.setXY.call(this, [x, y]);
8175             supr.setSize.call(this, w, h, a, d, cb, e);
8176             cb();
8177         }else{
8178             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8179         }
8180         return this;
8181     },
8182     
8183     /**
8184      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8185      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8186      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8187      * @param {Number} zindex The new z-index to set
8188      * @return {this} The Layer
8189      */
8190     setZIndex : function(zindex){
8191         this.zindex = zindex;
8192         this.setStyle("z-index", zindex + 2);
8193         if(this.shadow){
8194             this.shadow.setZIndex(zindex + 1);
8195         }
8196         if(this.shim){
8197             this.shim.setStyle("z-index", zindex);
8198         }
8199     }
8200 });
8201 })();/*
8202  * Based on:
8203  * Ext JS Library 1.1.1
8204  * Copyright(c) 2006-2007, Ext JS, LLC.
8205  *
8206  * Originally Released Under LGPL - original licence link has changed is not relivant.
8207  *
8208  * Fork - LGPL
8209  * <script type="text/javascript">
8210  */
8211
8212
8213 /**
8214  * @class Roo.Shadow
8215  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8216  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8217  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8218  * @constructor
8219  * Create a new Shadow
8220  * @param {Object} config The config object
8221  */
8222 Roo.Shadow = function(config){
8223     Roo.apply(this, config);
8224     if(typeof this.mode != "string"){
8225         this.mode = this.defaultMode;
8226     }
8227     var o = this.offset, a = {h: 0};
8228     var rad = Math.floor(this.offset/2);
8229     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8230         case "drop":
8231             a.w = 0;
8232             a.l = a.t = o;
8233             a.t -= 1;
8234             if(Roo.isIE){
8235                 a.l -= this.offset + rad;
8236                 a.t -= this.offset + rad;
8237                 a.w -= rad;
8238                 a.h -= rad;
8239                 a.t += 1;
8240             }
8241         break;
8242         case "sides":
8243             a.w = (o*2);
8244             a.l = -o;
8245             a.t = o-1;
8246             if(Roo.isIE){
8247                 a.l -= (this.offset - rad);
8248                 a.t -= this.offset + rad;
8249                 a.l += 1;
8250                 a.w -= (this.offset - rad)*2;
8251                 a.w -= rad + 1;
8252                 a.h -= 1;
8253             }
8254         break;
8255         case "frame":
8256             a.w = a.h = (o*2);
8257             a.l = a.t = -o;
8258             a.t += 1;
8259             a.h -= 2;
8260             if(Roo.isIE){
8261                 a.l -= (this.offset - rad);
8262                 a.t -= (this.offset - rad);
8263                 a.l += 1;
8264                 a.w -= (this.offset + rad + 1);
8265                 a.h -= (this.offset + rad);
8266                 a.h += 1;
8267             }
8268         break;
8269     };
8270
8271     this.adjusts = a;
8272 };
8273
8274 Roo.Shadow.prototype = {
8275     /**
8276      * @cfg {String} mode
8277      * The shadow display mode.  Supports the following options:<br />
8278      * sides: Shadow displays on both sides and bottom only<br />
8279      * frame: Shadow displays equally on all four sides<br />
8280      * drop: Traditional bottom-right drop shadow (default)
8281      */
8282     /**
8283      * @cfg {String} offset
8284      * The number of pixels to offset the shadow from the element (defaults to 4)
8285      */
8286     offset: 4,
8287
8288     // private
8289     defaultMode: "drop",
8290
8291     /**
8292      * Displays the shadow under the target element
8293      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8294      */
8295     show : function(target){
8296         target = Roo.get(target);
8297         if(!this.el){
8298             this.el = Roo.Shadow.Pool.pull();
8299             if(this.el.dom.nextSibling != target.dom){
8300                 this.el.insertBefore(target);
8301             }
8302         }
8303         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8304         if(Roo.isIE){
8305             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8306         }
8307         this.realign(
8308             target.getLeft(true),
8309             target.getTop(true),
8310             target.getWidth(),
8311             target.getHeight()
8312         );
8313         this.el.dom.style.display = "block";
8314     },
8315
8316     /**
8317      * Returns true if the shadow is visible, else false
8318      */
8319     isVisible : function(){
8320         return this.el ? true : false;  
8321     },
8322
8323     /**
8324      * Direct alignment when values are already available. Show must be called at least once before
8325      * calling this method to ensure it is initialized.
8326      * @param {Number} left The target element left position
8327      * @param {Number} top The target element top position
8328      * @param {Number} width The target element width
8329      * @param {Number} height The target element height
8330      */
8331     realign : function(l, t, w, h){
8332         if(!this.el){
8333             return;
8334         }
8335         var a = this.adjusts, d = this.el.dom, s = d.style;
8336         var iea = 0;
8337         s.left = (l+a.l)+"px";
8338         s.top = (t+a.t)+"px";
8339         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8340  
8341         if(s.width != sws || s.height != shs){
8342             s.width = sws;
8343             s.height = shs;
8344             if(!Roo.isIE){
8345                 var cn = d.childNodes;
8346                 var sww = Math.max(0, (sw-12))+"px";
8347                 cn[0].childNodes[1].style.width = sww;
8348                 cn[1].childNodes[1].style.width = sww;
8349                 cn[2].childNodes[1].style.width = sww;
8350                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8351             }
8352         }
8353     },
8354
8355     /**
8356      * Hides this shadow
8357      */
8358     hide : function(){
8359         if(this.el){
8360             this.el.dom.style.display = "none";
8361             Roo.Shadow.Pool.push(this.el);
8362             delete this.el;
8363         }
8364     },
8365
8366     /**
8367      * Adjust the z-index of this shadow
8368      * @param {Number} zindex The new z-index
8369      */
8370     setZIndex : function(z){
8371         this.zIndex = z;
8372         if(this.el){
8373             this.el.setStyle("z-index", z);
8374         }
8375     }
8376 };
8377
8378 // Private utility class that manages the internal Shadow cache
8379 Roo.Shadow.Pool = function(){
8380     var p = [];
8381     var markup = Roo.isIE ?
8382                  '<div class="x-ie-shadow"></div>' :
8383                  '<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>';
8384     return {
8385         pull : function(){
8386             var sh = p.shift();
8387             if(!sh){
8388                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8389                 sh.autoBoxAdjust = false;
8390             }
8391             return sh;
8392         },
8393
8394         push : function(sh){
8395             p.push(sh);
8396         }
8397     };
8398 }();/*
8399  * Based on:
8400  * Ext JS Library 1.1.1
8401  * Copyright(c) 2006-2007, Ext JS, LLC.
8402  *
8403  * Originally Released Under LGPL - original licence link has changed is not relivant.
8404  *
8405  * Fork - LGPL
8406  * <script type="text/javascript">
8407  */
8408
8409 /**
8410  * @class Roo.BoxComponent
8411  * @extends Roo.Component
8412  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8413  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8414  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8415  * layout containers.
8416  * @constructor
8417  * @param {Roo.Element/String/Object} config The configuration options.
8418  */
8419 Roo.BoxComponent = function(config){
8420     Roo.Component.call(this, config);
8421     this.addEvents({
8422         /**
8423          * @event resize
8424          * Fires after the component is resized.
8425              * @param {Roo.Component} this
8426              * @param {Number} adjWidth The box-adjusted width that was set
8427              * @param {Number} adjHeight The box-adjusted height that was set
8428              * @param {Number} rawWidth The width that was originally specified
8429              * @param {Number} rawHeight The height that was originally specified
8430              */
8431         resize : true,
8432         /**
8433          * @event move
8434          * Fires after the component is moved.
8435              * @param {Roo.Component} this
8436              * @param {Number} x The new x position
8437              * @param {Number} y The new y position
8438              */
8439         move : true
8440     });
8441 };
8442
8443 Roo.extend(Roo.BoxComponent, Roo.Component, {
8444     // private, set in afterRender to signify that the component has been rendered
8445     boxReady : false,
8446     // private, used to defer height settings to subclasses
8447     deferHeight: false,
8448     /** @cfg {Number} width
8449      * width (optional) size of component
8450      */
8451      /** @cfg {Number} height
8452      * height (optional) size of component
8453      */
8454      
8455     /**
8456      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8457      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8458      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8459      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8460      * @return {Roo.BoxComponent} this
8461      */
8462     setSize : function(w, h){
8463         // support for standard size objects
8464         if(typeof w == 'object'){
8465             h = w.height;
8466             w = w.width;
8467         }
8468         // not rendered
8469         if(!this.boxReady){
8470             this.width = w;
8471             this.height = h;
8472             return this;
8473         }
8474
8475         // prevent recalcs when not needed
8476         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8477             return this;
8478         }
8479         this.lastSize = {width: w, height: h};
8480
8481         var adj = this.adjustSize(w, h);
8482         var aw = adj.width, ah = adj.height;
8483         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8484             var rz = this.getResizeEl();
8485             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8486                 rz.setSize(aw, ah);
8487             }else if(!this.deferHeight && ah !== undefined){
8488                 rz.setHeight(ah);
8489             }else if(aw !== undefined){
8490                 rz.setWidth(aw);
8491             }
8492             this.onResize(aw, ah, w, h);
8493             this.fireEvent('resize', this, aw, ah, w, h);
8494         }
8495         return this;
8496     },
8497
8498     /**
8499      * Gets the current size of the component's underlying element.
8500      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8501      */
8502     getSize : function(){
8503         return this.el.getSize();
8504     },
8505
8506     /**
8507      * Gets the current XY position of the component's underlying element.
8508      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8509      * @return {Array} The XY position of the element (e.g., [100, 200])
8510      */
8511     getPosition : function(local){
8512         if(local === true){
8513             return [this.el.getLeft(true), this.el.getTop(true)];
8514         }
8515         return this.xy || this.el.getXY();
8516     },
8517
8518     /**
8519      * Gets the current box measurements of the component's underlying element.
8520      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8521      * @returns {Object} box An object in the format {x, y, width, height}
8522      */
8523     getBox : function(local){
8524         var s = this.el.getSize();
8525         if(local){
8526             s.x = this.el.getLeft(true);
8527             s.y = this.el.getTop(true);
8528         }else{
8529             var xy = this.xy || this.el.getXY();
8530             s.x = xy[0];
8531             s.y = xy[1];
8532         }
8533         return s;
8534     },
8535
8536     /**
8537      * Sets the current box measurements of the component's underlying element.
8538      * @param {Object} box An object in the format {x, y, width, height}
8539      * @returns {Roo.BoxComponent} this
8540      */
8541     updateBox : function(box){
8542         this.setSize(box.width, box.height);
8543         this.setPagePosition(box.x, box.y);
8544         return this;
8545     },
8546
8547     // protected
8548     getResizeEl : function(){
8549         return this.resizeEl || this.el;
8550     },
8551
8552     // protected
8553     getPositionEl : function(){
8554         return this.positionEl || this.el;
8555     },
8556
8557     /**
8558      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8559      * This method fires the move event.
8560      * @param {Number} left The new left
8561      * @param {Number} top The new top
8562      * @returns {Roo.BoxComponent} this
8563      */
8564     setPosition : function(x, y){
8565         this.x = x;
8566         this.y = y;
8567         if(!this.boxReady){
8568             return this;
8569         }
8570         var adj = this.adjustPosition(x, y);
8571         var ax = adj.x, ay = adj.y;
8572
8573         var el = this.getPositionEl();
8574         if(ax !== undefined || ay !== undefined){
8575             if(ax !== undefined && ay !== undefined){
8576                 el.setLeftTop(ax, ay);
8577             }else if(ax !== undefined){
8578                 el.setLeft(ax);
8579             }else if(ay !== undefined){
8580                 el.setTop(ay);
8581             }
8582             this.onPosition(ax, ay);
8583             this.fireEvent('move', this, ax, ay);
8584         }
8585         return this;
8586     },
8587
8588     /**
8589      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8590      * This method fires the move event.
8591      * @param {Number} x The new x position
8592      * @param {Number} y The new y position
8593      * @returns {Roo.BoxComponent} this
8594      */
8595     setPagePosition : function(x, y){
8596         this.pageX = x;
8597         this.pageY = y;
8598         if(!this.boxReady){
8599             return;
8600         }
8601         if(x === undefined || y === undefined){ // cannot translate undefined points
8602             return;
8603         }
8604         var p = this.el.translatePoints(x, y);
8605         this.setPosition(p.left, p.top);
8606         return this;
8607     },
8608
8609     // private
8610     onRender : function(ct, position){
8611         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8612         if(this.resizeEl){
8613             this.resizeEl = Roo.get(this.resizeEl);
8614         }
8615         if(this.positionEl){
8616             this.positionEl = Roo.get(this.positionEl);
8617         }
8618     },
8619
8620     // private
8621     afterRender : function(){
8622         Roo.BoxComponent.superclass.afterRender.call(this);
8623         this.boxReady = true;
8624         this.setSize(this.width, this.height);
8625         if(this.x || this.y){
8626             this.setPosition(this.x, this.y);
8627         }
8628         if(this.pageX || this.pageY){
8629             this.setPagePosition(this.pageX, this.pageY);
8630         }
8631     },
8632
8633     /**
8634      * Force the component's size to recalculate based on the underlying element's current height and width.
8635      * @returns {Roo.BoxComponent} this
8636      */
8637     syncSize : function(){
8638         delete this.lastSize;
8639         this.setSize(this.el.getWidth(), this.el.getHeight());
8640         return this;
8641     },
8642
8643     /**
8644      * Called after the component is resized, this method is empty by default but can be implemented by any
8645      * subclass that needs to perform custom logic after a resize occurs.
8646      * @param {Number} adjWidth The box-adjusted width that was set
8647      * @param {Number} adjHeight The box-adjusted height that was set
8648      * @param {Number} rawWidth The width that was originally specified
8649      * @param {Number} rawHeight The height that was originally specified
8650      */
8651     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8652
8653     },
8654
8655     /**
8656      * Called after the component is moved, this method is empty by default but can be implemented by any
8657      * subclass that needs to perform custom logic after a move occurs.
8658      * @param {Number} x The new x position
8659      * @param {Number} y The new y position
8660      */
8661     onPosition : function(x, y){
8662
8663     },
8664
8665     // private
8666     adjustSize : function(w, h){
8667         if(this.autoWidth){
8668             w = 'auto';
8669         }
8670         if(this.autoHeight){
8671             h = 'auto';
8672         }
8673         return {width : w, height: h};
8674     },
8675
8676     // private
8677     adjustPosition : function(x, y){
8678         return {x : x, y: y};
8679     }
8680 });/*
8681  * Based on:
8682  * Ext JS Library 1.1.1
8683  * Copyright(c) 2006-2007, Ext JS, LLC.
8684  *
8685  * Originally Released Under LGPL - original licence link has changed is not relivant.
8686  *
8687  * Fork - LGPL
8688  * <script type="text/javascript">
8689  */
8690
8691
8692 /**
8693  * @class Roo.SplitBar
8694  * @extends Roo.util.Observable
8695  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8696  * <br><br>
8697  * Usage:
8698  * <pre><code>
8699 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8700                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8701 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8702 split.minSize = 100;
8703 split.maxSize = 600;
8704 split.animate = true;
8705 split.on('moved', splitterMoved);
8706 </code></pre>
8707  * @constructor
8708  * Create a new SplitBar
8709  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8710  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8711  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8712  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8713                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8714                         position of the SplitBar).
8715  */
8716 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8717     
8718     /** @private */
8719     this.el = Roo.get(dragElement, true);
8720     this.el.dom.unselectable = "on";
8721     /** @private */
8722     this.resizingEl = Roo.get(resizingElement, true);
8723
8724     /**
8725      * @private
8726      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8727      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8728      * @type Number
8729      */
8730     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8731     
8732     /**
8733      * The minimum size of the resizing element. (Defaults to 0)
8734      * @type Number
8735      */
8736     this.minSize = 0;
8737     
8738     /**
8739      * The maximum size of the resizing element. (Defaults to 2000)
8740      * @type Number
8741      */
8742     this.maxSize = 2000;
8743     
8744     /**
8745      * Whether to animate the transition to the new size
8746      * @type Boolean
8747      */
8748     this.animate = false;
8749     
8750     /**
8751      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8752      * @type Boolean
8753      */
8754     this.useShim = false;
8755     
8756     /** @private */
8757     this.shim = null;
8758     
8759     if(!existingProxy){
8760         /** @private */
8761         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8762     }else{
8763         this.proxy = Roo.get(existingProxy).dom;
8764     }
8765     /** @private */
8766     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8767     
8768     /** @private */
8769     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8770     
8771     /** @private */
8772     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8773     
8774     /** @private */
8775     this.dragSpecs = {};
8776     
8777     /**
8778      * @private The adapter to use to positon and resize elements
8779      */
8780     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8781     this.adapter.init(this);
8782     
8783     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8784         /** @private */
8785         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8786         this.el.addClass("x-splitbar-h");
8787     }else{
8788         /** @private */
8789         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8790         this.el.addClass("x-splitbar-v");
8791     }
8792     
8793     this.addEvents({
8794         /**
8795          * @event resize
8796          * Fires when the splitter is moved (alias for {@link #event-moved})
8797          * @param {Roo.SplitBar} this
8798          * @param {Number} newSize the new width or height
8799          */
8800         "resize" : true,
8801         /**
8802          * @event moved
8803          * Fires when the splitter is moved
8804          * @param {Roo.SplitBar} this
8805          * @param {Number} newSize the new width or height
8806          */
8807         "moved" : true,
8808         /**
8809          * @event beforeresize
8810          * Fires before the splitter is dragged
8811          * @param {Roo.SplitBar} this
8812          */
8813         "beforeresize" : true,
8814
8815         "beforeapply" : true
8816     });
8817
8818     Roo.util.Observable.call(this);
8819 };
8820
8821 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8822     onStartProxyDrag : function(x, y){
8823         this.fireEvent("beforeresize", this);
8824         if(!this.overlay){
8825             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8826             o.unselectable();
8827             o.enableDisplayMode("block");
8828             // all splitbars share the same overlay
8829             Roo.SplitBar.prototype.overlay = o;
8830         }
8831         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8832         this.overlay.show();
8833         Roo.get(this.proxy).setDisplayed("block");
8834         var size = this.adapter.getElementSize(this);
8835         this.activeMinSize = this.getMinimumSize();;
8836         this.activeMaxSize = this.getMaximumSize();;
8837         var c1 = size - this.activeMinSize;
8838         var c2 = Math.max(this.activeMaxSize - size, 0);
8839         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8840             this.dd.resetConstraints();
8841             this.dd.setXConstraint(
8842                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8843                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8844             );
8845             this.dd.setYConstraint(0, 0);
8846         }else{
8847             this.dd.resetConstraints();
8848             this.dd.setXConstraint(0, 0);
8849             this.dd.setYConstraint(
8850                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8851                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8852             );
8853          }
8854         this.dragSpecs.startSize = size;
8855         this.dragSpecs.startPoint = [x, y];
8856         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8857     },
8858     
8859     /** 
8860      * @private Called after the drag operation by the DDProxy
8861      */
8862     onEndProxyDrag : function(e){
8863         Roo.get(this.proxy).setDisplayed(false);
8864         var endPoint = Roo.lib.Event.getXY(e);
8865         if(this.overlay){
8866             this.overlay.hide();
8867         }
8868         var newSize;
8869         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8870             newSize = this.dragSpecs.startSize + 
8871                 (this.placement == Roo.SplitBar.LEFT ?
8872                     endPoint[0] - this.dragSpecs.startPoint[0] :
8873                     this.dragSpecs.startPoint[0] - endPoint[0]
8874                 );
8875         }else{
8876             newSize = this.dragSpecs.startSize + 
8877                 (this.placement == Roo.SplitBar.TOP ?
8878                     endPoint[1] - this.dragSpecs.startPoint[1] :
8879                     this.dragSpecs.startPoint[1] - endPoint[1]
8880                 );
8881         }
8882         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8883         if(newSize != this.dragSpecs.startSize){
8884             if(this.fireEvent('beforeapply', this, newSize) !== false){
8885                 this.adapter.setElementSize(this, newSize);
8886                 this.fireEvent("moved", this, newSize);
8887                 this.fireEvent("resize", this, newSize);
8888             }
8889         }
8890     },
8891     
8892     /**
8893      * Get the adapter this SplitBar uses
8894      * @return The adapter object
8895      */
8896     getAdapter : function(){
8897         return this.adapter;
8898     },
8899     
8900     /**
8901      * Set the adapter this SplitBar uses
8902      * @param {Object} adapter A SplitBar adapter object
8903      */
8904     setAdapter : function(adapter){
8905         this.adapter = adapter;
8906         this.adapter.init(this);
8907     },
8908     
8909     /**
8910      * Gets the minimum size for the resizing element
8911      * @return {Number} The minimum size
8912      */
8913     getMinimumSize : function(){
8914         return this.minSize;
8915     },
8916     
8917     /**
8918      * Sets the minimum size for the resizing element
8919      * @param {Number} minSize The minimum size
8920      */
8921     setMinimumSize : function(minSize){
8922         this.minSize = minSize;
8923     },
8924     
8925     /**
8926      * Gets the maximum size for the resizing element
8927      * @return {Number} The maximum size
8928      */
8929     getMaximumSize : function(){
8930         return this.maxSize;
8931     },
8932     
8933     /**
8934      * Sets the maximum size for the resizing element
8935      * @param {Number} maxSize The maximum size
8936      */
8937     setMaximumSize : function(maxSize){
8938         this.maxSize = maxSize;
8939     },
8940     
8941     /**
8942      * Sets the initialize size for the resizing element
8943      * @param {Number} size The initial size
8944      */
8945     setCurrentSize : function(size){
8946         var oldAnimate = this.animate;
8947         this.animate = false;
8948         this.adapter.setElementSize(this, size);
8949         this.animate = oldAnimate;
8950     },
8951     
8952     /**
8953      * Destroy this splitbar. 
8954      * @param {Boolean} removeEl True to remove the element
8955      */
8956     destroy : function(removeEl){
8957         if(this.shim){
8958             this.shim.remove();
8959         }
8960         this.dd.unreg();
8961         this.proxy.parentNode.removeChild(this.proxy);
8962         if(removeEl){
8963             this.el.remove();
8964         }
8965     }
8966 });
8967
8968 /**
8969  * @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.
8970  */
8971 Roo.SplitBar.createProxy = function(dir){
8972     var proxy = new Roo.Element(document.createElement("div"));
8973     proxy.unselectable();
8974     var cls = 'x-splitbar-proxy';
8975     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8976     document.body.appendChild(proxy.dom);
8977     return proxy.dom;
8978 };
8979
8980 /** 
8981  * @class Roo.SplitBar.BasicLayoutAdapter
8982  * Default Adapter. It assumes the splitter and resizing element are not positioned
8983  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8984  */
8985 Roo.SplitBar.BasicLayoutAdapter = function(){
8986 };
8987
8988 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8989     // do nothing for now
8990     init : function(s){
8991     
8992     },
8993     /**
8994      * Called before drag operations to get the current size of the resizing element. 
8995      * @param {Roo.SplitBar} s The SplitBar using this adapter
8996      */
8997      getElementSize : function(s){
8998         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8999             return s.resizingEl.getWidth();
9000         }else{
9001             return s.resizingEl.getHeight();
9002         }
9003     },
9004     
9005     /**
9006      * Called after drag operations to set the size of the resizing element.
9007      * @param {Roo.SplitBar} s The SplitBar using this adapter
9008      * @param {Number} newSize The new size to set
9009      * @param {Function} onComplete A function to be invoked when resizing is complete
9010      */
9011     setElementSize : function(s, newSize, onComplete){
9012         if(s.orientation == Roo.SplitBar.HORIZONTAL){
9013             if(!s.animate){
9014                 s.resizingEl.setWidth(newSize);
9015                 if(onComplete){
9016                     onComplete(s, newSize);
9017                 }
9018             }else{
9019                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
9020             }
9021         }else{
9022             
9023             if(!s.animate){
9024                 s.resizingEl.setHeight(newSize);
9025                 if(onComplete){
9026                     onComplete(s, newSize);
9027                 }
9028             }else{
9029                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9030             }
9031         }
9032     }
9033 };
9034
9035 /** 
9036  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9037  * @extends Roo.SplitBar.BasicLayoutAdapter
9038  * Adapter that  moves the splitter element to align with the resized sizing element. 
9039  * Used with an absolute positioned SplitBar.
9040  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9041  * document.body, make sure you assign an id to the body element.
9042  */
9043 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9044     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9045     this.container = Roo.get(container);
9046 };
9047
9048 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9049     init : function(s){
9050         this.basic.init(s);
9051     },
9052     
9053     getElementSize : function(s){
9054         return this.basic.getElementSize(s);
9055     },
9056     
9057     setElementSize : function(s, newSize, onComplete){
9058         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9059     },
9060     
9061     moveSplitter : function(s){
9062         var yes = Roo.SplitBar;
9063         switch(s.placement){
9064             case yes.LEFT:
9065                 s.el.setX(s.resizingEl.getRight());
9066                 break;
9067             case yes.RIGHT:
9068                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9069                 break;
9070             case yes.TOP:
9071                 s.el.setY(s.resizingEl.getBottom());
9072                 break;
9073             case yes.BOTTOM:
9074                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9075                 break;
9076         }
9077     }
9078 };
9079
9080 /**
9081  * Orientation constant - Create a vertical SplitBar
9082  * @static
9083  * @type Number
9084  */
9085 Roo.SplitBar.VERTICAL = 1;
9086
9087 /**
9088  * Orientation constant - Create a horizontal SplitBar
9089  * @static
9090  * @type Number
9091  */
9092 Roo.SplitBar.HORIZONTAL = 2;
9093
9094 /**
9095  * Placement constant - The resizing element is to the left of the splitter element
9096  * @static
9097  * @type Number
9098  */
9099 Roo.SplitBar.LEFT = 1;
9100
9101 /**
9102  * Placement constant - The resizing element is to the right of the splitter element
9103  * @static
9104  * @type Number
9105  */
9106 Roo.SplitBar.RIGHT = 2;
9107
9108 /**
9109  * Placement constant - The resizing element is positioned above the splitter element
9110  * @static
9111  * @type Number
9112  */
9113 Roo.SplitBar.TOP = 3;
9114
9115 /**
9116  * Placement constant - The resizing element is positioned under splitter element
9117  * @static
9118  * @type Number
9119  */
9120 Roo.SplitBar.BOTTOM = 4;
9121 /*
9122  * Based on:
9123  * Ext JS Library 1.1.1
9124  * Copyright(c) 2006-2007, Ext JS, LLC.
9125  *
9126  * Originally Released Under LGPL - original licence link has changed is not relivant.
9127  *
9128  * Fork - LGPL
9129  * <script type="text/javascript">
9130  */
9131
9132 /**
9133  * @class Roo.View
9134  * @extends Roo.util.Observable
9135  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9136  * This class also supports single and multi selection modes. <br>
9137  * Create a data model bound view:
9138  <pre><code>
9139  var store = new Roo.data.Store(...);
9140
9141  var view = new Roo.View({
9142     el : "my-element",
9143     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9144  
9145     singleSelect: true,
9146     selectedClass: "ydataview-selected",
9147     store: store
9148  });
9149
9150  // listen for node click?
9151  view.on("click", function(vw, index, node, e){
9152  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9153  });
9154
9155  // load XML data
9156  dataModel.load("foobar.xml");
9157  </code></pre>
9158  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9159  * <br><br>
9160  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9161  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9162  * 
9163  * Note: old style constructor is still suported (container, template, config)
9164  * 
9165  * @constructor
9166  * Create a new View
9167  * @param {Object} config The config object
9168  * 
9169  */
9170 Roo.View = function(config, depreciated_tpl, depreciated_config){
9171     
9172     if (typeof(depreciated_tpl) == 'undefined') {
9173         // new way.. - universal constructor.
9174         Roo.apply(this, config);
9175         this.el  = Roo.get(this.el);
9176     } else {
9177         // old format..
9178         this.el  = Roo.get(config);
9179         this.tpl = depreciated_tpl;
9180         Roo.apply(this, depreciated_config);
9181     }
9182     this.wrapEl  = this.el.wrap().wrap();
9183     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9184     
9185     
9186     if(typeof(this.tpl) == "string"){
9187         this.tpl = new Roo.Template(this.tpl);
9188     } else {
9189         // support xtype ctors..
9190         this.tpl = new Roo.factory(this.tpl, Roo);
9191     }
9192     
9193     
9194     this.tpl.compile();
9195    
9196   
9197     
9198      
9199     /** @private */
9200     this.addEvents({
9201         /**
9202          * @event beforeclick
9203          * Fires before a click is processed. Returns false to cancel the default action.
9204          * @param {Roo.View} this
9205          * @param {Number} index The index of the target node
9206          * @param {HTMLElement} node The target node
9207          * @param {Roo.EventObject} e The raw event object
9208          */
9209             "beforeclick" : true,
9210         /**
9211          * @event click
9212          * Fires when a template node is clicked.
9213          * @param {Roo.View} this
9214          * @param {Number} index The index of the target node
9215          * @param {HTMLElement} node The target node
9216          * @param {Roo.EventObject} e The raw event object
9217          */
9218             "click" : true,
9219         /**
9220          * @event dblclick
9221          * Fires when a template node is double clicked.
9222          * @param {Roo.View} this
9223          * @param {Number} index The index of the target node
9224          * @param {HTMLElement} node The target node
9225          * @param {Roo.EventObject} e The raw event object
9226          */
9227             "dblclick" : true,
9228         /**
9229          * @event contextmenu
9230          * Fires when a template node is right clicked.
9231          * @param {Roo.View} this
9232          * @param {Number} index The index of the target node
9233          * @param {HTMLElement} node The target node
9234          * @param {Roo.EventObject} e The raw event object
9235          */
9236             "contextmenu" : true,
9237         /**
9238          * @event selectionchange
9239          * Fires when the selected nodes change.
9240          * @param {Roo.View} this
9241          * @param {Array} selections Array of the selected nodes
9242          */
9243             "selectionchange" : true,
9244     
9245         /**
9246          * @event beforeselect
9247          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9248          * @param {Roo.View} this
9249          * @param {HTMLElement} node The node to be selected
9250          * @param {Array} selections Array of currently selected nodes
9251          */
9252             "beforeselect" : true,
9253         /**
9254          * @event preparedata
9255          * Fires on every row to render, to allow you to change the data.
9256          * @param {Roo.View} this
9257          * @param {Object} data to be rendered (change this)
9258          */
9259           "preparedata" : true
9260           
9261           
9262         });
9263
9264
9265
9266     this.el.on({
9267         "click": this.onClick,
9268         "dblclick": this.onDblClick,
9269         "contextmenu": this.onContextMenu,
9270         scope:this
9271     });
9272
9273     this.selections = [];
9274     this.nodes = [];
9275     this.cmp = new Roo.CompositeElementLite([]);
9276     if(this.store){
9277         this.store = Roo.factory(this.store, Roo.data);
9278         this.setStore(this.store, true);
9279     }
9280     
9281     if ( this.footer && this.footer.xtype) {
9282            
9283          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9284         
9285         this.footer.dataSource = this.store
9286         this.footer.container = fctr;
9287         this.footer = Roo.factory(this.footer, Roo);
9288         fctr.insertFirst(this.el);
9289         
9290         // this is a bit insane - as the paging toolbar seems to detach the el..
9291 //        dom.parentNode.parentNode.parentNode
9292          // they get detached?
9293     }
9294     
9295     
9296     Roo.View.superclass.constructor.call(this);
9297     
9298     
9299 };
9300
9301 Roo.extend(Roo.View, Roo.util.Observable, {
9302     
9303      /**
9304      * @cfg {Roo.data.Store} store Data store to load data from.
9305      */
9306     store : false,
9307     
9308     /**
9309      * @cfg {String|Roo.Element} el The container element.
9310      */
9311     el : '',
9312     
9313     /**
9314      * @cfg {String|Roo.Template} tpl The template used by this View 
9315      */
9316     tpl : false,
9317     /**
9318      * @cfg {String} dataName the named area of the template to use as the data area
9319      *                          Works with domtemplates roo-name="name"
9320      */
9321     dataName: false,
9322     /**
9323      * @cfg {String} selectedClass The css class to add to selected nodes
9324      */
9325     selectedClass : "x-view-selected",
9326      /**
9327      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9328      */
9329     emptyText : "",
9330     
9331     /**
9332      * @cfg {String} text to display on mask (default Loading)
9333      */
9334     mask : false,
9335     /**
9336      * @cfg {Boolean} multiSelect Allow multiple selection
9337      */
9338     multiSelect : false,
9339     /**
9340      * @cfg {Boolean} singleSelect Allow single selection
9341      */
9342     singleSelect:  false,
9343     
9344     /**
9345      * @cfg {Boolean} toggleSelect - selecting 
9346      */
9347     toggleSelect : false,
9348     
9349     /**
9350      * Returns the element this view is bound to.
9351      * @return {Roo.Element}
9352      */
9353     getEl : function(){
9354         return this.wrapEl;
9355     },
9356     
9357     
9358
9359     /**
9360      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9361      */
9362     refresh : function(){
9363         var t = this.tpl;
9364         
9365         // if we are using something like 'domtemplate', then
9366         // the what gets used is:
9367         // t.applySubtemplate(NAME, data, wrapping data..)
9368         // the outer template then get' applied with
9369         //     the store 'extra data'
9370         // and the body get's added to the
9371         //      roo-name="data" node?
9372         //      <span class='roo-tpl-{name}'></span> ?????
9373         
9374         
9375         
9376         this.clearSelections();
9377         this.el.update("");
9378         var html = [];
9379         var records = this.store.getRange();
9380         if(records.length < 1) {
9381             
9382             // is this valid??  = should it render a template??
9383             
9384             this.el.update(this.emptyText);
9385             return;
9386         }
9387         var el = this.el;
9388         if (this.dataName) {
9389             this.el.update(t.apply(this.store.meta)); //????
9390             el = this.el.child('.roo-tpl-' + this.dataName);
9391         }
9392         
9393         for(var i = 0, len = records.length; i < len; i++){
9394             var data = this.prepareData(records[i].data, i, records[i]);
9395             this.fireEvent("preparedata", this, data, i, records[i]);
9396             html[html.length] = Roo.util.Format.trim(
9397                 this.dataName ?
9398                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9399                     t.apply(data)
9400             );
9401         }
9402         
9403         
9404         
9405         el.update(html.join(""));
9406         this.nodes = el.dom.childNodes;
9407         this.updateIndexes(0);
9408     },
9409
9410     /**
9411      * Function to override to reformat the data that is sent to
9412      * the template for each node.
9413      * DEPRICATED - use the preparedata event handler.
9414      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9415      * a JSON object for an UpdateManager bound view).
9416      */
9417     prepareData : function(data, index, record)
9418     {
9419         this.fireEvent("preparedata", this, data, index, record);
9420         return data;
9421     },
9422
9423     onUpdate : function(ds, record){
9424         this.clearSelections();
9425         var index = this.store.indexOf(record);
9426         var n = this.nodes[index];
9427         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9428         n.parentNode.removeChild(n);
9429         this.updateIndexes(index, index);
9430     },
9431
9432     
9433     
9434 // --------- FIXME     
9435     onAdd : function(ds, records, index)
9436     {
9437         this.clearSelections();
9438         if(this.nodes.length == 0){
9439             this.refresh();
9440             return;
9441         }
9442         var n = this.nodes[index];
9443         for(var i = 0, len = records.length; i < len; i++){
9444             var d = this.prepareData(records[i].data, i, records[i]);
9445             if(n){
9446                 this.tpl.insertBefore(n, d);
9447             }else{
9448                 
9449                 this.tpl.append(this.el, d);
9450             }
9451         }
9452         this.updateIndexes(index);
9453     },
9454
9455     onRemove : function(ds, record, index){
9456         this.clearSelections();
9457         var el = this.dataName  ?
9458             this.el.child('.roo-tpl-' + this.dataName) :
9459             this.el; 
9460         el.dom.removeChild(this.nodes[index]);
9461         this.updateIndexes(index);
9462     },
9463
9464     /**
9465      * Refresh an individual node.
9466      * @param {Number} index
9467      */
9468     refreshNode : function(index){
9469         this.onUpdate(this.store, this.store.getAt(index));
9470     },
9471
9472     updateIndexes : function(startIndex, endIndex){
9473         var ns = this.nodes;
9474         startIndex = startIndex || 0;
9475         endIndex = endIndex || ns.length - 1;
9476         for(var i = startIndex; i <= endIndex; i++){
9477             ns[i].nodeIndex = i;
9478         }
9479     },
9480
9481     /**
9482      * Changes the data store this view uses and refresh the view.
9483      * @param {Store} store
9484      */
9485     setStore : function(store, initial){
9486         if(!initial && this.store){
9487             this.store.un("datachanged", this.refresh);
9488             this.store.un("add", this.onAdd);
9489             this.store.un("remove", this.onRemove);
9490             this.store.un("update", this.onUpdate);
9491             this.store.un("clear", this.refresh);
9492             this.store.un("beforeload", this.onBeforeLoad);
9493             this.store.un("load", this.onLoad);
9494             this.store.un("loadexception", this.onLoad);
9495         }
9496         if(store){
9497           
9498             store.on("datachanged", this.refresh, this);
9499             store.on("add", this.onAdd, this);
9500             store.on("remove", this.onRemove, this);
9501             store.on("update", this.onUpdate, this);
9502             store.on("clear", this.refresh, this);
9503             store.on("beforeload", this.onBeforeLoad, this);
9504             store.on("load", this.onLoad, this);
9505             store.on("loadexception", this.onLoad, this);
9506         }
9507         
9508         if(store){
9509             this.refresh();
9510         }
9511     },
9512     /**
9513      * onbeforeLoad - masks the loading area.
9514      *
9515      */
9516     onBeforeLoad : function()
9517     {
9518         this.el.update("");
9519         this.el.mask(this.mask ? this.mask : "Loading" ); 
9520     },
9521     onLoad : function ()
9522     {
9523         this.el.unmask();
9524     },
9525     
9526
9527     /**
9528      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9529      * @param {HTMLElement} node
9530      * @return {HTMLElement} The template node
9531      */
9532     findItemFromChild : function(node){
9533         var el = this.dataName  ?
9534             this.el.child('.roo-tpl-' + this.dataName,true) :
9535             this.el.dom; 
9536         
9537         if(!node || node.parentNode == el){
9538                     return node;
9539             }
9540             var p = node.parentNode;
9541             while(p && p != el){
9542             if(p.parentNode == el){
9543                 return p;
9544             }
9545             p = p.parentNode;
9546         }
9547             return null;
9548     },
9549
9550     /** @ignore */
9551     onClick : function(e){
9552         var item = this.findItemFromChild(e.getTarget());
9553         if(item){
9554             var index = this.indexOf(item);
9555             if(this.onItemClick(item, index, e) !== false){
9556                 this.fireEvent("click", this, index, item, e);
9557             }
9558         }else{
9559             this.clearSelections();
9560         }
9561     },
9562
9563     /** @ignore */
9564     onContextMenu : function(e){
9565         var item = this.findItemFromChild(e.getTarget());
9566         if(item){
9567             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9568         }
9569     },
9570
9571     /** @ignore */
9572     onDblClick : function(e){
9573         var item = this.findItemFromChild(e.getTarget());
9574         if(item){
9575             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9576         }
9577     },
9578
9579     onItemClick : function(item, index, e)
9580     {
9581         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9582             return false;
9583         }
9584         if (this.toggleSelect) {
9585             var m = this.isSelected(item) ? 'unselect' : 'select';
9586             Roo.log(m);
9587             var _t = this;
9588             _t[m](item, true, false);
9589             return true;
9590         }
9591         if(this.multiSelect || this.singleSelect){
9592             if(this.multiSelect && e.shiftKey && this.lastSelection){
9593                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9594             }else{
9595                 this.select(item, this.multiSelect && e.ctrlKey);
9596                 this.lastSelection = item;
9597             }
9598             e.preventDefault();
9599         }
9600         return true;
9601     },
9602
9603     /**
9604      * Get the number of selected nodes.
9605      * @return {Number}
9606      */
9607     getSelectionCount : function(){
9608         return this.selections.length;
9609     },
9610
9611     /**
9612      * Get the currently selected nodes.
9613      * @return {Array} An array of HTMLElements
9614      */
9615     getSelectedNodes : function(){
9616         return this.selections;
9617     },
9618
9619     /**
9620      * Get the indexes of the selected nodes.
9621      * @return {Array}
9622      */
9623     getSelectedIndexes : function(){
9624         var indexes = [], s = this.selections;
9625         for(var i = 0, len = s.length; i < len; i++){
9626             indexes.push(s[i].nodeIndex);
9627         }
9628         return indexes;
9629     },
9630
9631     /**
9632      * Clear all selections
9633      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9634      */
9635     clearSelections : function(suppressEvent){
9636         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9637             this.cmp.elements = this.selections;
9638             this.cmp.removeClass(this.selectedClass);
9639             this.selections = [];
9640             if(!suppressEvent){
9641                 this.fireEvent("selectionchange", this, this.selections);
9642             }
9643         }
9644     },
9645
9646     /**
9647      * Returns true if the passed node is selected
9648      * @param {HTMLElement/Number} node The node or node index
9649      * @return {Boolean}
9650      */
9651     isSelected : function(node){
9652         var s = this.selections;
9653         if(s.length < 1){
9654             return false;
9655         }
9656         node = this.getNode(node);
9657         return s.indexOf(node) !== -1;
9658     },
9659
9660     /**
9661      * Selects nodes.
9662      * @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
9663      * @param {Boolean} keepExisting (optional) true to keep existing selections
9664      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9665      */
9666     select : function(nodeInfo, keepExisting, suppressEvent){
9667         if(nodeInfo instanceof Array){
9668             if(!keepExisting){
9669                 this.clearSelections(true);
9670             }
9671             for(var i = 0, len = nodeInfo.length; i < len; i++){
9672                 this.select(nodeInfo[i], true, true);
9673             }
9674             return;
9675         } 
9676         var node = this.getNode(nodeInfo);
9677         if(!node || this.isSelected(node)){
9678             return; // already selected.
9679         }
9680         if(!keepExisting){
9681             this.clearSelections(true);
9682         }
9683         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9684             Roo.fly(node).addClass(this.selectedClass);
9685             this.selections.push(node);
9686             if(!suppressEvent){
9687                 this.fireEvent("selectionchange", this, this.selections);
9688             }
9689         }
9690         
9691         
9692     },
9693       /**
9694      * Unselects nodes.
9695      * @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
9696      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9697      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9698      */
9699     unselect : function(nodeInfo, keepExisting, suppressEvent)
9700     {
9701         if(nodeInfo instanceof Array){
9702             Roo.each(this.selections, function(s) {
9703                 this.unselect(s, nodeInfo);
9704             }, this);
9705             return;
9706         }
9707         var node = this.getNode(nodeInfo);
9708         if(!node || !this.isSelected(node)){
9709             Roo.log("not selected");
9710             return; // not selected.
9711         }
9712         // fireevent???
9713         var ns = [];
9714         Roo.each(this.selections, function(s) {
9715             if (s == node ) {
9716                 Roo.fly(node).removeClass(this.selectedClass);
9717
9718                 return;
9719             }
9720             ns.push(s);
9721         },this);
9722         
9723         this.selections= ns;
9724         this.fireEvent("selectionchange", this, this.selections);
9725     },
9726
9727     /**
9728      * Gets a template node.
9729      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9730      * @return {HTMLElement} The node or null if it wasn't found
9731      */
9732     getNode : function(nodeInfo){
9733         if(typeof nodeInfo == "string"){
9734             return document.getElementById(nodeInfo);
9735         }else if(typeof nodeInfo == "number"){
9736             return this.nodes[nodeInfo];
9737         }
9738         return nodeInfo;
9739     },
9740
9741     /**
9742      * Gets a range template nodes.
9743      * @param {Number} startIndex
9744      * @param {Number} endIndex
9745      * @return {Array} An array of nodes
9746      */
9747     getNodes : function(start, end){
9748         var ns = this.nodes;
9749         start = start || 0;
9750         end = typeof end == "undefined" ? ns.length - 1 : end;
9751         var nodes = [];
9752         if(start <= end){
9753             for(var i = start; i <= end; i++){
9754                 nodes.push(ns[i]);
9755             }
9756         } else{
9757             for(var i = start; i >= end; i--){
9758                 nodes.push(ns[i]);
9759             }
9760         }
9761         return nodes;
9762     },
9763
9764     /**
9765      * Finds the index of the passed node
9766      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9767      * @return {Number} The index of the node or -1
9768      */
9769     indexOf : function(node){
9770         node = this.getNode(node);
9771         if(typeof node.nodeIndex == "number"){
9772             return node.nodeIndex;
9773         }
9774         var ns = this.nodes;
9775         for(var i = 0, len = ns.length; i < len; i++){
9776             if(ns[i] == node){
9777                 return i;
9778             }
9779         }
9780         return -1;
9781     }
9782 });
9783 /*
9784  * Based on:
9785  * Ext JS Library 1.1.1
9786  * Copyright(c) 2006-2007, Ext JS, LLC.
9787  *
9788  * Originally Released Under LGPL - original licence link has changed is not relivant.
9789  *
9790  * Fork - LGPL
9791  * <script type="text/javascript">
9792  */
9793
9794 /**
9795  * @class Roo.JsonView
9796  * @extends Roo.View
9797  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9798 <pre><code>
9799 var view = new Roo.JsonView({
9800     container: "my-element",
9801     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9802     multiSelect: true, 
9803     jsonRoot: "data" 
9804 });
9805
9806 // listen for node click?
9807 view.on("click", function(vw, index, node, e){
9808     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9809 });
9810
9811 // direct load of JSON data
9812 view.load("foobar.php");
9813
9814 // Example from my blog list
9815 var tpl = new Roo.Template(
9816     '&lt;div class="entry"&gt;' +
9817     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9818     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9819     "&lt;/div&gt;&lt;hr /&gt;"
9820 );
9821
9822 var moreView = new Roo.JsonView({
9823     container :  "entry-list", 
9824     template : tpl,
9825     jsonRoot: "posts"
9826 });
9827 moreView.on("beforerender", this.sortEntries, this);
9828 moreView.load({
9829     url: "/blog/get-posts.php",
9830     params: "allposts=true",
9831     text: "Loading Blog Entries..."
9832 });
9833 </code></pre>
9834
9835 * Note: old code is supported with arguments : (container, template, config)
9836
9837
9838  * @constructor
9839  * Create a new JsonView
9840  * 
9841  * @param {Object} config The config object
9842  * 
9843  */
9844 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9845     
9846     
9847     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9848
9849     var um = this.el.getUpdateManager();
9850     um.setRenderer(this);
9851     um.on("update", this.onLoad, this);
9852     um.on("failure", this.onLoadException, this);
9853
9854     /**
9855      * @event beforerender
9856      * Fires before rendering of the downloaded JSON data.
9857      * @param {Roo.JsonView} this
9858      * @param {Object} data The JSON data loaded
9859      */
9860     /**
9861      * @event load
9862      * Fires when data is loaded.
9863      * @param {Roo.JsonView} this
9864      * @param {Object} data The JSON data loaded
9865      * @param {Object} response The raw Connect response object
9866      */
9867     /**
9868      * @event loadexception
9869      * Fires when loading fails.
9870      * @param {Roo.JsonView} this
9871      * @param {Object} response The raw Connect response object
9872      */
9873     this.addEvents({
9874         'beforerender' : true,
9875         'load' : true,
9876         'loadexception' : true
9877     });
9878 };
9879 Roo.extend(Roo.JsonView, Roo.View, {
9880     /**
9881      * @type {String} The root property in the loaded JSON object that contains the data
9882      */
9883     jsonRoot : "",
9884
9885     /**
9886      * Refreshes the view.
9887      */
9888     refresh : function(){
9889         this.clearSelections();
9890         this.el.update("");
9891         var html = [];
9892         var o = this.jsonData;
9893         if(o && o.length > 0){
9894             for(var i = 0, len = o.length; i < len; i++){
9895                 var data = this.prepareData(o[i], i, o);
9896                 html[html.length] = this.tpl.apply(data);
9897             }
9898         }else{
9899             html.push(this.emptyText);
9900         }
9901         this.el.update(html.join(""));
9902         this.nodes = this.el.dom.childNodes;
9903         this.updateIndexes(0);
9904     },
9905
9906     /**
9907      * 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.
9908      * @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:
9909      <pre><code>
9910      view.load({
9911          url: "your-url.php",
9912          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9913          callback: yourFunction,
9914          scope: yourObject, //(optional scope)
9915          discardUrl: false,
9916          nocache: false,
9917          text: "Loading...",
9918          timeout: 30,
9919          scripts: false
9920      });
9921      </code></pre>
9922      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9923      * 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.
9924      * @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}
9925      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9926      * @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.
9927      */
9928     load : function(){
9929         var um = this.el.getUpdateManager();
9930         um.update.apply(um, arguments);
9931     },
9932
9933     render : function(el, response){
9934         this.clearSelections();
9935         this.el.update("");
9936         var o;
9937         try{
9938             o = Roo.util.JSON.decode(response.responseText);
9939             if(this.jsonRoot){
9940                 
9941                 o = o[this.jsonRoot];
9942             }
9943         } catch(e){
9944         }
9945         /**
9946          * The current JSON data or null
9947          */
9948         this.jsonData = o;
9949         this.beforeRender();
9950         this.refresh();
9951     },
9952
9953 /**
9954  * Get the number of records in the current JSON dataset
9955  * @return {Number}
9956  */
9957     getCount : function(){
9958         return this.jsonData ? this.jsonData.length : 0;
9959     },
9960
9961 /**
9962  * Returns the JSON object for the specified node(s)
9963  * @param {HTMLElement/Array} node The node or an array of nodes
9964  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9965  * you get the JSON object for the node
9966  */
9967     getNodeData : function(node){
9968         if(node instanceof Array){
9969             var data = [];
9970             for(var i = 0, len = node.length; i < len; i++){
9971                 data.push(this.getNodeData(node[i]));
9972             }
9973             return data;
9974         }
9975         return this.jsonData[this.indexOf(node)] || null;
9976     },
9977
9978     beforeRender : function(){
9979         this.snapshot = this.jsonData;
9980         if(this.sortInfo){
9981             this.sort.apply(this, this.sortInfo);
9982         }
9983         this.fireEvent("beforerender", this, this.jsonData);
9984     },
9985
9986     onLoad : function(el, o){
9987         this.fireEvent("load", this, this.jsonData, o);
9988     },
9989
9990     onLoadException : function(el, o){
9991         this.fireEvent("loadexception", this, o);
9992     },
9993
9994 /**
9995  * Filter the data by a specific property.
9996  * @param {String} property A property on your JSON objects
9997  * @param {String/RegExp} value Either string that the property values
9998  * should start with, or a RegExp to test against the property
9999  */
10000     filter : function(property, value){
10001         if(this.jsonData){
10002             var data = [];
10003             var ss = this.snapshot;
10004             if(typeof value == "string"){
10005                 var vlen = value.length;
10006                 if(vlen == 0){
10007                     this.clearFilter();
10008                     return;
10009                 }
10010                 value = value.toLowerCase();
10011                 for(var i = 0, len = ss.length; i < len; i++){
10012                     var o = ss[i];
10013                     if(o[property].substr(0, vlen).toLowerCase() == value){
10014                         data.push(o);
10015                     }
10016                 }
10017             } else if(value.exec){ // regex?
10018                 for(var i = 0, len = ss.length; i < len; i++){
10019                     var o = ss[i];
10020                     if(value.test(o[property])){
10021                         data.push(o);
10022                     }
10023                 }
10024             } else{
10025                 return;
10026             }
10027             this.jsonData = data;
10028             this.refresh();
10029         }
10030     },
10031
10032 /**
10033  * Filter by a function. The passed function will be called with each
10034  * object in the current dataset. If the function returns true the value is kept,
10035  * otherwise it is filtered.
10036  * @param {Function} fn
10037  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
10038  */
10039     filterBy : function(fn, scope){
10040         if(this.jsonData){
10041             var data = [];
10042             var ss = this.snapshot;
10043             for(var i = 0, len = ss.length; i < len; i++){
10044                 var o = ss[i];
10045                 if(fn.call(scope || this, o)){
10046                     data.push(o);
10047                 }
10048             }
10049             this.jsonData = data;
10050             this.refresh();
10051         }
10052     },
10053
10054 /**
10055  * Clears the current filter.
10056  */
10057     clearFilter : function(){
10058         if(this.snapshot && this.jsonData != this.snapshot){
10059             this.jsonData = this.snapshot;
10060             this.refresh();
10061         }
10062     },
10063
10064
10065 /**
10066  * Sorts the data for this view and refreshes it.
10067  * @param {String} property A property on your JSON objects to sort on
10068  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
10069  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
10070  */
10071     sort : function(property, dir, sortType){
10072         this.sortInfo = Array.prototype.slice.call(arguments, 0);
10073         if(this.jsonData){
10074             var p = property;
10075             var dsc = dir && dir.toLowerCase() == "desc";
10076             var f = function(o1, o2){
10077                 var v1 = sortType ? sortType(o1[p]) : o1[p];
10078                 var v2 = sortType ? sortType(o2[p]) : o2[p];
10079                 ;
10080                 if(v1 < v2){
10081                     return dsc ? +1 : -1;
10082                 } else if(v1 > v2){
10083                     return dsc ? -1 : +1;
10084                 } else{
10085                     return 0;
10086                 }
10087             };
10088             this.jsonData.sort(f);
10089             this.refresh();
10090             if(this.jsonData != this.snapshot){
10091                 this.snapshot.sort(f);
10092             }
10093         }
10094     }
10095 });/*
10096  * Based on:
10097  * Ext JS Library 1.1.1
10098  * Copyright(c) 2006-2007, Ext JS, LLC.
10099  *
10100  * Originally Released Under LGPL - original licence link has changed is not relivant.
10101  *
10102  * Fork - LGPL
10103  * <script type="text/javascript">
10104  */
10105  
10106
10107 /**
10108  * @class Roo.ColorPalette
10109  * @extends Roo.Component
10110  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10111  * Here's an example of typical usage:
10112  * <pre><code>
10113 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10114 cp.render('my-div');
10115
10116 cp.on('select', function(palette, selColor){
10117     // do something with selColor
10118 });
10119 </code></pre>
10120  * @constructor
10121  * Create a new ColorPalette
10122  * @param {Object} config The config object
10123  */
10124 Roo.ColorPalette = function(config){
10125     Roo.ColorPalette.superclass.constructor.call(this, config);
10126     this.addEvents({
10127         /**
10128              * @event select
10129              * Fires when a color is selected
10130              * @param {ColorPalette} this
10131              * @param {String} color The 6-digit color hex code (without the # symbol)
10132              */
10133         select: true
10134     });
10135
10136     if(this.handler){
10137         this.on("select", this.handler, this.scope, true);
10138     }
10139 };
10140 Roo.extend(Roo.ColorPalette, Roo.Component, {
10141     /**
10142      * @cfg {String} itemCls
10143      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10144      */
10145     itemCls : "x-color-palette",
10146     /**
10147      * @cfg {String} value
10148      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10149      * the hex codes are case-sensitive.
10150      */
10151     value : null,
10152     clickEvent:'click',
10153     // private
10154     ctype: "Roo.ColorPalette",
10155
10156     /**
10157      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10158      */
10159     allowReselect : false,
10160
10161     /**
10162      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10163      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10164      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10165      * of colors with the width setting until the box is symmetrical.</p>
10166      * <p>You can override individual colors if needed:</p>
10167      * <pre><code>
10168 var cp = new Roo.ColorPalette();
10169 cp.colors[0] = "FF0000";  // change the first box to red
10170 </code></pre>
10171
10172 Or you can provide a custom array of your own for complete control:
10173 <pre><code>
10174 var cp = new Roo.ColorPalette();
10175 cp.colors = ["000000", "993300", "333300"];
10176 </code></pre>
10177      * @type Array
10178      */
10179     colors : [
10180         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10181         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10182         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10183         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10184         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10185     ],
10186
10187     // private
10188     onRender : function(container, position){
10189         var t = new Roo.MasterTemplate(
10190             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10191         );
10192         var c = this.colors;
10193         for(var i = 0, len = c.length; i < len; i++){
10194             t.add([c[i]]);
10195         }
10196         var el = document.createElement("div");
10197         el.className = this.itemCls;
10198         t.overwrite(el);
10199         container.dom.insertBefore(el, position);
10200         this.el = Roo.get(el);
10201         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10202         if(this.clickEvent != 'click'){
10203             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10204         }
10205     },
10206
10207     // private
10208     afterRender : function(){
10209         Roo.ColorPalette.superclass.afterRender.call(this);
10210         if(this.value){
10211             var s = this.value;
10212             this.value = null;
10213             this.select(s);
10214         }
10215     },
10216
10217     // private
10218     handleClick : function(e, t){
10219         e.preventDefault();
10220         if(!this.disabled){
10221             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10222             this.select(c.toUpperCase());
10223         }
10224     },
10225
10226     /**
10227      * Selects the specified color in the palette (fires the select event)
10228      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10229      */
10230     select : function(color){
10231         color = color.replace("#", "");
10232         if(color != this.value || this.allowReselect){
10233             var el = this.el;
10234             if(this.value){
10235                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10236             }
10237             el.child("a.color-"+color).addClass("x-color-palette-sel");
10238             this.value = color;
10239             this.fireEvent("select", this, color);
10240         }
10241     }
10242 });/*
10243  * Based on:
10244  * Ext JS Library 1.1.1
10245  * Copyright(c) 2006-2007, Ext JS, LLC.
10246  *
10247  * Originally Released Under LGPL - original licence link has changed is not relivant.
10248  *
10249  * Fork - LGPL
10250  * <script type="text/javascript">
10251  */
10252  
10253 /**
10254  * @class Roo.DatePicker
10255  * @extends Roo.Component
10256  * Simple date picker class.
10257  * @constructor
10258  * Create a new DatePicker
10259  * @param {Object} config The config object
10260  */
10261 Roo.DatePicker = function(config){
10262     Roo.DatePicker.superclass.constructor.call(this, config);
10263
10264     this.value = config && config.value ?
10265                  config.value.clearTime() : new Date().clearTime();
10266
10267     this.addEvents({
10268         /**
10269              * @event select
10270              * Fires when a date is selected
10271              * @param {DatePicker} this
10272              * @param {Date} date The selected date
10273              */
10274         'select': true,
10275         /**
10276              * @event monthchange
10277              * Fires when the displayed month changes 
10278              * @param {DatePicker} this
10279              * @param {Date} date The selected month
10280              */
10281         'monthchange': true
10282     });
10283
10284     if(this.handler){
10285         this.on("select", this.handler,  this.scope || this);
10286     }
10287     // build the disabledDatesRE
10288     if(!this.disabledDatesRE && this.disabledDates){
10289         var dd = this.disabledDates;
10290         var re = "(?:";
10291         for(var i = 0; i < dd.length; i++){
10292             re += dd[i];
10293             if(i != dd.length-1) re += "|";
10294         }
10295         this.disabledDatesRE = new RegExp(re + ")");
10296     }
10297 };
10298
10299 Roo.extend(Roo.DatePicker, Roo.Component, {
10300     /**
10301      * @cfg {String} todayText
10302      * The text to display on the button that selects the current date (defaults to "Today")
10303      */
10304     todayText : "Today",
10305     /**
10306      * @cfg {String} okText
10307      * The text to display on the ok button
10308      */
10309     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10310     /**
10311      * @cfg {String} cancelText
10312      * The text to display on the cancel button
10313      */
10314     cancelText : "Cancel",
10315     /**
10316      * @cfg {String} todayTip
10317      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10318      */
10319     todayTip : "{0} (Spacebar)",
10320     /**
10321      * @cfg {Date} minDate
10322      * Minimum allowable date (JavaScript date object, defaults to null)
10323      */
10324     minDate : null,
10325     /**
10326      * @cfg {Date} maxDate
10327      * Maximum allowable date (JavaScript date object, defaults to null)
10328      */
10329     maxDate : null,
10330     /**
10331      * @cfg {String} minText
10332      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10333      */
10334     minText : "This date is before the minimum date",
10335     /**
10336      * @cfg {String} maxText
10337      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10338      */
10339     maxText : "This date is after the maximum date",
10340     /**
10341      * @cfg {String} format
10342      * The default date format string which can be overriden for localization support.  The format must be
10343      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10344      */
10345     format : "m/d/y",
10346     /**
10347      * @cfg {Array} disabledDays
10348      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10349      */
10350     disabledDays : null,
10351     /**
10352      * @cfg {String} disabledDaysText
10353      * The tooltip to display when the date falls on a disabled day (defaults to "")
10354      */
10355     disabledDaysText : "",
10356     /**
10357      * @cfg {RegExp} disabledDatesRE
10358      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10359      */
10360     disabledDatesRE : null,
10361     /**
10362      * @cfg {String} disabledDatesText
10363      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10364      */
10365     disabledDatesText : "",
10366     /**
10367      * @cfg {Boolean} constrainToViewport
10368      * True to constrain the date picker to the viewport (defaults to true)
10369      */
10370     constrainToViewport : true,
10371     /**
10372      * @cfg {Array} monthNames
10373      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10374      */
10375     monthNames : Date.monthNames,
10376     /**
10377      * @cfg {Array} dayNames
10378      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10379      */
10380     dayNames : Date.dayNames,
10381     /**
10382      * @cfg {String} nextText
10383      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10384      */
10385     nextText: 'Next Month (Control+Right)',
10386     /**
10387      * @cfg {String} prevText
10388      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10389      */
10390     prevText: 'Previous Month (Control+Left)',
10391     /**
10392      * @cfg {String} monthYearText
10393      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10394      */
10395     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10396     /**
10397      * @cfg {Number} startDay
10398      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10399      */
10400     startDay : 0,
10401     /**
10402      * @cfg {Bool} showClear
10403      * Show a clear button (usefull for date form elements that can be blank.)
10404      */
10405     
10406     showClear: false,
10407     
10408     /**
10409      * Sets the value of the date field
10410      * @param {Date} value The date to set
10411      */
10412     setValue : function(value){
10413         var old = this.value;
10414         
10415         if (typeof(value) == 'string') {
10416          
10417             value = Date.parseDate(value, this.format);
10418         }
10419         if (!value) {
10420             value = new Date();
10421         }
10422         
10423         this.value = value.clearTime(true);
10424         if(this.el){
10425             this.update(this.value);
10426         }
10427     },
10428
10429     /**
10430      * Gets the current selected value of the date field
10431      * @return {Date} The selected date
10432      */
10433     getValue : function(){
10434         return this.value;
10435     },
10436
10437     // private
10438     focus : function(){
10439         if(this.el){
10440             this.update(this.activeDate);
10441         }
10442     },
10443
10444     // privateval
10445     onRender : function(container, position){
10446         
10447         var m = [
10448              '<table cellspacing="0">',
10449                 '<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>',
10450                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10451         var dn = this.dayNames;
10452         for(var i = 0; i < 7; i++){
10453             var d = this.startDay+i;
10454             if(d > 6){
10455                 d = d-7;
10456             }
10457             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10458         }
10459         m[m.length] = "</tr></thead><tbody><tr>";
10460         for(var i = 0; i < 42; i++) {
10461             if(i % 7 == 0 && i != 0){
10462                 m[m.length] = "</tr><tr>";
10463             }
10464             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10465         }
10466         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10467             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10468
10469         var el = document.createElement("div");
10470         el.className = "x-date-picker";
10471         el.innerHTML = m.join("");
10472
10473         container.dom.insertBefore(el, position);
10474
10475         this.el = Roo.get(el);
10476         this.eventEl = Roo.get(el.firstChild);
10477
10478         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10479             handler: this.showPrevMonth,
10480             scope: this,
10481             preventDefault:true,
10482             stopDefault:true
10483         });
10484
10485         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10486             handler: this.showNextMonth,
10487             scope: this,
10488             preventDefault:true,
10489             stopDefault:true
10490         });
10491
10492         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10493
10494         this.monthPicker = this.el.down('div.x-date-mp');
10495         this.monthPicker.enableDisplayMode('block');
10496         
10497         var kn = new Roo.KeyNav(this.eventEl, {
10498             "left" : function(e){
10499                 e.ctrlKey ?
10500                     this.showPrevMonth() :
10501                     this.update(this.activeDate.add("d", -1));
10502             },
10503
10504             "right" : function(e){
10505                 e.ctrlKey ?
10506                     this.showNextMonth() :
10507                     this.update(this.activeDate.add("d", 1));
10508             },
10509
10510             "up" : function(e){
10511                 e.ctrlKey ?
10512                     this.showNextYear() :
10513                     this.update(this.activeDate.add("d", -7));
10514             },
10515
10516             "down" : function(e){
10517                 e.ctrlKey ?
10518                     this.showPrevYear() :
10519                     this.update(this.activeDate.add("d", 7));
10520             },
10521
10522             "pageUp" : function(e){
10523                 this.showNextMonth();
10524             },
10525
10526             "pageDown" : function(e){
10527                 this.showPrevMonth();
10528             },
10529
10530             "enter" : function(e){
10531                 e.stopPropagation();
10532                 return true;
10533             },
10534
10535             scope : this
10536         });
10537
10538         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10539
10540         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10541
10542         this.el.unselectable();
10543         
10544         this.cells = this.el.select("table.x-date-inner tbody td");
10545         this.textNodes = this.el.query("table.x-date-inner tbody span");
10546
10547         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10548             text: "&#160;",
10549             tooltip: this.monthYearText
10550         });
10551
10552         this.mbtn.on('click', this.showMonthPicker, this);
10553         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10554
10555
10556         var today = (new Date()).dateFormat(this.format);
10557         
10558         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10559         if (this.showClear) {
10560             baseTb.add( new Roo.Toolbar.Fill());
10561         }
10562         baseTb.add({
10563             text: String.format(this.todayText, today),
10564             tooltip: String.format(this.todayTip, today),
10565             handler: this.selectToday,
10566             scope: this
10567         });
10568         
10569         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10570             
10571         //});
10572         if (this.showClear) {
10573             
10574             baseTb.add( new Roo.Toolbar.Fill());
10575             baseTb.add({
10576                 text: '&#160;',
10577                 cls: 'x-btn-icon x-btn-clear',
10578                 handler: function() {
10579                     //this.value = '';
10580                     this.fireEvent("select", this, '');
10581                 },
10582                 scope: this
10583             });
10584         }
10585         
10586         
10587         if(Roo.isIE){
10588             this.el.repaint();
10589         }
10590         this.update(this.value);
10591     },
10592
10593     createMonthPicker : function(){
10594         if(!this.monthPicker.dom.firstChild){
10595             var buf = ['<table border="0" cellspacing="0">'];
10596             for(var i = 0; i < 6; i++){
10597                 buf.push(
10598                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10599                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10600                     i == 0 ?
10601                     '<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>' :
10602                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10603                 );
10604             }
10605             buf.push(
10606                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10607                     this.okText,
10608                     '</button><button type="button" class="x-date-mp-cancel">',
10609                     this.cancelText,
10610                     '</button></td></tr>',
10611                 '</table>'
10612             );
10613             this.monthPicker.update(buf.join(''));
10614             this.monthPicker.on('click', this.onMonthClick, this);
10615             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10616
10617             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10618             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10619
10620             this.mpMonths.each(function(m, a, i){
10621                 i += 1;
10622                 if((i%2) == 0){
10623                     m.dom.xmonth = 5 + Math.round(i * .5);
10624                 }else{
10625                     m.dom.xmonth = Math.round((i-1) * .5);
10626                 }
10627             });
10628         }
10629     },
10630
10631     showMonthPicker : function(){
10632         this.createMonthPicker();
10633         var size = this.el.getSize();
10634         this.monthPicker.setSize(size);
10635         this.monthPicker.child('table').setSize(size);
10636
10637         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10638         this.updateMPMonth(this.mpSelMonth);
10639         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10640         this.updateMPYear(this.mpSelYear);
10641
10642         this.monthPicker.slideIn('t', {duration:.2});
10643     },
10644
10645     updateMPYear : function(y){
10646         this.mpyear = y;
10647         var ys = this.mpYears.elements;
10648         for(var i = 1; i <= 10; i++){
10649             var td = ys[i-1], y2;
10650             if((i%2) == 0){
10651                 y2 = y + Math.round(i * .5);
10652                 td.firstChild.innerHTML = y2;
10653                 td.xyear = y2;
10654             }else{
10655                 y2 = y - (5-Math.round(i * .5));
10656                 td.firstChild.innerHTML = y2;
10657                 td.xyear = y2;
10658             }
10659             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10660         }
10661     },
10662
10663     updateMPMonth : function(sm){
10664         this.mpMonths.each(function(m, a, i){
10665             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10666         });
10667     },
10668
10669     selectMPMonth: function(m){
10670         
10671     },
10672
10673     onMonthClick : function(e, t){
10674         e.stopEvent();
10675         var el = new Roo.Element(t), pn;
10676         if(el.is('button.x-date-mp-cancel')){
10677             this.hideMonthPicker();
10678         }
10679         else if(el.is('button.x-date-mp-ok')){
10680             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10681             this.hideMonthPicker();
10682         }
10683         else if(pn = el.up('td.x-date-mp-month', 2)){
10684             this.mpMonths.removeClass('x-date-mp-sel');
10685             pn.addClass('x-date-mp-sel');
10686             this.mpSelMonth = pn.dom.xmonth;
10687         }
10688         else if(pn = el.up('td.x-date-mp-year', 2)){
10689             this.mpYears.removeClass('x-date-mp-sel');
10690             pn.addClass('x-date-mp-sel');
10691             this.mpSelYear = pn.dom.xyear;
10692         }
10693         else if(el.is('a.x-date-mp-prev')){
10694             this.updateMPYear(this.mpyear-10);
10695         }
10696         else if(el.is('a.x-date-mp-next')){
10697             this.updateMPYear(this.mpyear+10);
10698         }
10699     },
10700
10701     onMonthDblClick : function(e, t){
10702         e.stopEvent();
10703         var el = new Roo.Element(t), pn;
10704         if(pn = el.up('td.x-date-mp-month', 2)){
10705             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10706             this.hideMonthPicker();
10707         }
10708         else if(pn = el.up('td.x-date-mp-year', 2)){
10709             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10710             this.hideMonthPicker();
10711         }
10712     },
10713
10714     hideMonthPicker : function(disableAnim){
10715         if(this.monthPicker){
10716             if(disableAnim === true){
10717                 this.monthPicker.hide();
10718             }else{
10719                 this.monthPicker.slideOut('t', {duration:.2});
10720             }
10721         }
10722     },
10723
10724     // private
10725     showPrevMonth : function(e){
10726         this.update(this.activeDate.add("mo", -1));
10727     },
10728
10729     // private
10730     showNextMonth : function(e){
10731         this.update(this.activeDate.add("mo", 1));
10732     },
10733
10734     // private
10735     showPrevYear : function(){
10736         this.update(this.activeDate.add("y", -1));
10737     },
10738
10739     // private
10740     showNextYear : function(){
10741         this.update(this.activeDate.add("y", 1));
10742     },
10743
10744     // private
10745     handleMouseWheel : function(e){
10746         var delta = e.getWheelDelta();
10747         if(delta > 0){
10748             this.showPrevMonth();
10749             e.stopEvent();
10750         } else if(delta < 0){
10751             this.showNextMonth();
10752             e.stopEvent();
10753         }
10754     },
10755
10756     // private
10757     handleDateClick : function(e, t){
10758         e.stopEvent();
10759         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10760             this.setValue(new Date(t.dateValue));
10761             this.fireEvent("select", this, this.value);
10762         }
10763     },
10764
10765     // private
10766     selectToday : function(){
10767         this.setValue(new Date().clearTime());
10768         this.fireEvent("select", this, this.value);
10769     },
10770
10771     // private
10772     update : function(date)
10773     {
10774         var vd = this.activeDate;
10775         this.activeDate = date;
10776         if(vd && this.el){
10777             var t = date.getTime();
10778             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10779                 this.cells.removeClass("x-date-selected");
10780                 this.cells.each(function(c){
10781                    if(c.dom.firstChild.dateValue == t){
10782                        c.addClass("x-date-selected");
10783                        setTimeout(function(){
10784                             try{c.dom.firstChild.focus();}catch(e){}
10785                        }, 50);
10786                        return false;
10787                    }
10788                 });
10789                 return;
10790             }
10791         }
10792         
10793         var days = date.getDaysInMonth();
10794         var firstOfMonth = date.getFirstDateOfMonth();
10795         var startingPos = firstOfMonth.getDay()-this.startDay;
10796
10797         if(startingPos <= this.startDay){
10798             startingPos += 7;
10799         }
10800
10801         var pm = date.add("mo", -1);
10802         var prevStart = pm.getDaysInMonth()-startingPos;
10803
10804         var cells = this.cells.elements;
10805         var textEls = this.textNodes;
10806         days += startingPos;
10807
10808         // convert everything to numbers so it's fast
10809         var day = 86400000;
10810         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10811         var today = new Date().clearTime().getTime();
10812         var sel = date.clearTime().getTime();
10813         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10814         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10815         var ddMatch = this.disabledDatesRE;
10816         var ddText = this.disabledDatesText;
10817         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10818         var ddaysText = this.disabledDaysText;
10819         var format = this.format;
10820
10821         var setCellClass = function(cal, cell){
10822             cell.title = "";
10823             var t = d.getTime();
10824             cell.firstChild.dateValue = t;
10825             if(t == today){
10826                 cell.className += " x-date-today";
10827                 cell.title = cal.todayText;
10828             }
10829             if(t == sel){
10830                 cell.className += " x-date-selected";
10831                 setTimeout(function(){
10832                     try{cell.firstChild.focus();}catch(e){}
10833                 }, 50);
10834             }
10835             // disabling
10836             if(t < min) {
10837                 cell.className = " x-date-disabled";
10838                 cell.title = cal.minText;
10839                 return;
10840             }
10841             if(t > max) {
10842                 cell.className = " x-date-disabled";
10843                 cell.title = cal.maxText;
10844                 return;
10845             }
10846             if(ddays){
10847                 if(ddays.indexOf(d.getDay()) != -1){
10848                     cell.title = ddaysText;
10849                     cell.className = " x-date-disabled";
10850                 }
10851             }
10852             if(ddMatch && format){
10853                 var fvalue = d.dateFormat(format);
10854                 if(ddMatch.test(fvalue)){
10855                     cell.title = ddText.replace("%0", fvalue);
10856                     cell.className = " x-date-disabled";
10857                 }
10858             }
10859         };
10860
10861         var i = 0;
10862         for(; i < startingPos; i++) {
10863             textEls[i].innerHTML = (++prevStart);
10864             d.setDate(d.getDate()+1);
10865             cells[i].className = "x-date-prevday";
10866             setCellClass(this, cells[i]);
10867         }
10868         for(; i < days; i++){
10869             intDay = i - startingPos + 1;
10870             textEls[i].innerHTML = (intDay);
10871             d.setDate(d.getDate()+1);
10872             cells[i].className = "x-date-active";
10873             setCellClass(this, cells[i]);
10874         }
10875         var extraDays = 0;
10876         for(; i < 42; i++) {
10877              textEls[i].innerHTML = (++extraDays);
10878              d.setDate(d.getDate()+1);
10879              cells[i].className = "x-date-nextday";
10880              setCellClass(this, cells[i]);
10881         }
10882
10883         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10884         this.fireEvent('monthchange', this, date);
10885         
10886         if(!this.internalRender){
10887             var main = this.el.dom.firstChild;
10888             var w = main.offsetWidth;
10889             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10890             Roo.fly(main).setWidth(w);
10891             this.internalRender = true;
10892             // opera does not respect the auto grow header center column
10893             // then, after it gets a width opera refuses to recalculate
10894             // without a second pass
10895             if(Roo.isOpera && !this.secondPass){
10896                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10897                 this.secondPass = true;
10898                 this.update.defer(10, this, [date]);
10899             }
10900         }
10901         
10902         
10903     }
10904 });        /*
10905  * Based on:
10906  * Ext JS Library 1.1.1
10907  * Copyright(c) 2006-2007, Ext JS, LLC.
10908  *
10909  * Originally Released Under LGPL - original licence link has changed is not relivant.
10910  *
10911  * Fork - LGPL
10912  * <script type="text/javascript">
10913  */
10914 /**
10915  * @class Roo.TabPanel
10916  * @extends Roo.util.Observable
10917  * A lightweight tab container.
10918  * <br><br>
10919  * Usage:
10920  * <pre><code>
10921 // basic tabs 1, built from existing content
10922 var tabs = new Roo.TabPanel("tabs1");
10923 tabs.addTab("script", "View Script");
10924 tabs.addTab("markup", "View Markup");
10925 tabs.activate("script");
10926
10927 // more advanced tabs, built from javascript
10928 var jtabs = new Roo.TabPanel("jtabs");
10929 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10930
10931 // set up the UpdateManager
10932 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10933 var updater = tab2.getUpdateManager();
10934 updater.setDefaultUrl("ajax1.htm");
10935 tab2.on('activate', updater.refresh, updater, true);
10936
10937 // Use setUrl for Ajax loading
10938 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10939 tab3.setUrl("ajax2.htm", null, true);
10940
10941 // Disabled tab
10942 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10943 tab4.disable();
10944
10945 jtabs.activate("jtabs-1");
10946  * </code></pre>
10947  * @constructor
10948  * Create a new TabPanel.
10949  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10950  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10951  */
10952 Roo.TabPanel = function(container, config){
10953     /**
10954     * The container element for this TabPanel.
10955     * @type Roo.Element
10956     */
10957     this.el = Roo.get(container, true);
10958     if(config){
10959         if(typeof config == "boolean"){
10960             this.tabPosition = config ? "bottom" : "top";
10961         }else{
10962             Roo.apply(this, config);
10963         }
10964     }
10965     if(this.tabPosition == "bottom"){
10966         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10967         this.el.addClass("x-tabs-bottom");
10968     }
10969     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10970     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10971     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10972     if(Roo.isIE){
10973         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10974     }
10975     if(this.tabPosition != "bottom"){
10976         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10977          * @type Roo.Element
10978          */
10979         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10980         this.el.addClass("x-tabs-top");
10981     }
10982     this.items = [];
10983
10984     this.bodyEl.setStyle("position", "relative");
10985
10986     this.active = null;
10987     this.activateDelegate = this.activate.createDelegate(this);
10988
10989     this.addEvents({
10990         /**
10991          * @event tabchange
10992          * Fires when the active tab changes
10993          * @param {Roo.TabPanel} this
10994          * @param {Roo.TabPanelItem} activePanel The new active tab
10995          */
10996         "tabchange": true,
10997         /**
10998          * @event beforetabchange
10999          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
11000          * @param {Roo.TabPanel} this
11001          * @param {Object} e Set cancel to true on this object to cancel the tab change
11002          * @param {Roo.TabPanelItem} tab The tab being changed to
11003          */
11004         "beforetabchange" : true
11005     });
11006
11007     Roo.EventManager.onWindowResize(this.onResize, this);
11008     this.cpad = this.el.getPadding("lr");
11009     this.hiddenCount = 0;
11010
11011
11012     // toolbar on the tabbar support...
11013     if (this.toolbar) {
11014         var tcfg = this.toolbar;
11015         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
11016         this.toolbar = new Roo.Toolbar(tcfg);
11017         if (Roo.isSafari) {
11018             var tbl = tcfg.container.child('table', true);
11019             tbl.setAttribute('width', '100%');
11020         }
11021         
11022     }
11023    
11024
11025
11026     Roo.TabPanel.superclass.constructor.call(this);
11027 };
11028
11029 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
11030     /*
11031      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
11032      */
11033     tabPosition : "top",
11034     /*
11035      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
11036      */
11037     currentTabWidth : 0,
11038     /*
11039      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
11040      */
11041     minTabWidth : 40,
11042     /*
11043      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
11044      */
11045     maxTabWidth : 250,
11046     /*
11047      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
11048      */
11049     preferredTabWidth : 175,
11050     /*
11051      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
11052      */
11053     resizeTabs : false,
11054     /*
11055      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
11056      */
11057     monitorResize : true,
11058     /*
11059      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
11060      */
11061     toolbar : false,
11062
11063     /**
11064      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
11065      * @param {String} id The id of the div to use <b>or create</b>
11066      * @param {String} text The text for the tab
11067      * @param {String} content (optional) Content to put in the TabPanelItem body
11068      * @param {Boolean} closable (optional) True to create a close icon on the tab
11069      * @return {Roo.TabPanelItem} The created TabPanelItem
11070      */
11071     addTab : function(id, text, content, closable){
11072         var item = new Roo.TabPanelItem(this, id, text, closable);
11073         this.addTabItem(item);
11074         if(content){
11075             item.setContent(content);
11076         }
11077         return item;
11078     },
11079
11080     /**
11081      * Returns the {@link Roo.TabPanelItem} with the specified id/index
11082      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
11083      * @return {Roo.TabPanelItem}
11084      */
11085     getTab : function(id){
11086         return this.items[id];
11087     },
11088
11089     /**
11090      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11091      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11092      */
11093     hideTab : function(id){
11094         var t = this.items[id];
11095         if(!t.isHidden()){
11096            t.setHidden(true);
11097            this.hiddenCount++;
11098            this.autoSizeTabs();
11099         }
11100     },
11101
11102     /**
11103      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11104      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11105      */
11106     unhideTab : function(id){
11107         var t = this.items[id];
11108         if(t.isHidden()){
11109            t.setHidden(false);
11110            this.hiddenCount--;
11111            this.autoSizeTabs();
11112         }
11113     },
11114
11115     /**
11116      * Adds an existing {@link Roo.TabPanelItem}.
11117      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11118      */
11119     addTabItem : function(item){
11120         this.items[item.id] = item;
11121         this.items.push(item);
11122         if(this.resizeTabs){
11123            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11124            this.autoSizeTabs();
11125         }else{
11126             item.autoSize();
11127         }
11128     },
11129
11130     /**
11131      * Removes a {@link Roo.TabPanelItem}.
11132      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11133      */
11134     removeTab : function(id){
11135         var items = this.items;
11136         var tab = items[id];
11137         if(!tab) { return; }
11138         var index = items.indexOf(tab);
11139         if(this.active == tab && items.length > 1){
11140             var newTab = this.getNextAvailable(index);
11141             if(newTab) {
11142                 newTab.activate();
11143             }
11144         }
11145         this.stripEl.dom.removeChild(tab.pnode.dom);
11146         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11147             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11148         }
11149         items.splice(index, 1);
11150         delete this.items[tab.id];
11151         tab.fireEvent("close", tab);
11152         tab.purgeListeners();
11153         this.autoSizeTabs();
11154     },
11155
11156     getNextAvailable : function(start){
11157         var items = this.items;
11158         var index = start;
11159         // look for a next tab that will slide over to
11160         // replace the one being removed
11161         while(index < items.length){
11162             var item = items[++index];
11163             if(item && !item.isHidden()){
11164                 return item;
11165             }
11166         }
11167         // if one isn't found select the previous tab (on the left)
11168         index = start;
11169         while(index >= 0){
11170             var item = items[--index];
11171             if(item && !item.isHidden()){
11172                 return item;
11173             }
11174         }
11175         return null;
11176     },
11177
11178     /**
11179      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11180      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11181      */
11182     disableTab : function(id){
11183         var tab = this.items[id];
11184         if(tab && this.active != tab){
11185             tab.disable();
11186         }
11187     },
11188
11189     /**
11190      * Enables a {@link Roo.TabPanelItem} that is disabled.
11191      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11192      */
11193     enableTab : function(id){
11194         var tab = this.items[id];
11195         tab.enable();
11196     },
11197
11198     /**
11199      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11200      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11201      * @return {Roo.TabPanelItem} The TabPanelItem.
11202      */
11203     activate : function(id){
11204         var tab = this.items[id];
11205         if(!tab){
11206             return null;
11207         }
11208         if(tab == this.active || tab.disabled){
11209             return tab;
11210         }
11211         var e = {};
11212         this.fireEvent("beforetabchange", this, e, tab);
11213         if(e.cancel !== true && !tab.disabled){
11214             if(this.active){
11215                 this.active.hide();
11216             }
11217             this.active = this.items[id];
11218             this.active.show();
11219             this.fireEvent("tabchange", this, this.active);
11220         }
11221         return tab;
11222     },
11223
11224     /**
11225      * Gets the active {@link Roo.TabPanelItem}.
11226      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11227      */
11228     getActiveTab : function(){
11229         return this.active;
11230     },
11231
11232     /**
11233      * Updates the tab body element to fit the height of the container element
11234      * for overflow scrolling
11235      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11236      */
11237     syncHeight : function(targetHeight){
11238         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11239         var bm = this.bodyEl.getMargins();
11240         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11241         this.bodyEl.setHeight(newHeight);
11242         return newHeight;
11243     },
11244
11245     onResize : function(){
11246         if(this.monitorResize){
11247             this.autoSizeTabs();
11248         }
11249     },
11250
11251     /**
11252      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11253      */
11254     beginUpdate : function(){
11255         this.updating = true;
11256     },
11257
11258     /**
11259      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11260      */
11261     endUpdate : function(){
11262         this.updating = false;
11263         this.autoSizeTabs();
11264     },
11265
11266     /**
11267      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11268      */
11269     autoSizeTabs : function(){
11270         var count = this.items.length;
11271         var vcount = count - this.hiddenCount;
11272         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11273         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11274         var availWidth = Math.floor(w / vcount);
11275         var b = this.stripBody;
11276         if(b.getWidth() > w){
11277             var tabs = this.items;
11278             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11279             if(availWidth < this.minTabWidth){
11280                 /*if(!this.sleft){    // incomplete scrolling code
11281                     this.createScrollButtons();
11282                 }
11283                 this.showScroll();
11284                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11285             }
11286         }else{
11287             if(this.currentTabWidth < this.preferredTabWidth){
11288                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11289             }
11290         }
11291     },
11292
11293     /**
11294      * Returns the number of tabs in this TabPanel.
11295      * @return {Number}
11296      */
11297      getCount : function(){
11298          return this.items.length;
11299      },
11300
11301     /**
11302      * Resizes all the tabs to the passed width
11303      * @param {Number} The new width
11304      */
11305     setTabWidth : function(width){
11306         this.currentTabWidth = width;
11307         for(var i = 0, len = this.items.length; i < len; i++) {
11308                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11309         }
11310     },
11311
11312     /**
11313      * Destroys this TabPanel
11314      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11315      */
11316     destroy : function(removeEl){
11317         Roo.EventManager.removeResizeListener(this.onResize, this);
11318         for(var i = 0, len = this.items.length; i < len; i++){
11319             this.items[i].purgeListeners();
11320         }
11321         if(removeEl === true){
11322             this.el.update("");
11323             this.el.remove();
11324         }
11325     }
11326 });
11327
11328 /**
11329  * @class Roo.TabPanelItem
11330  * @extends Roo.util.Observable
11331  * Represents an individual item (tab plus body) in a TabPanel.
11332  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11333  * @param {String} id The id of this TabPanelItem
11334  * @param {String} text The text for the tab of this TabPanelItem
11335  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11336  */
11337 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11338     /**
11339      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11340      * @type Roo.TabPanel
11341      */
11342     this.tabPanel = tabPanel;
11343     /**
11344      * The id for this TabPanelItem
11345      * @type String
11346      */
11347     this.id = id;
11348     /** @private */
11349     this.disabled = false;
11350     /** @private */
11351     this.text = text;
11352     /** @private */
11353     this.loaded = false;
11354     this.closable = closable;
11355
11356     /**
11357      * The body element for this TabPanelItem.
11358      * @type Roo.Element
11359      */
11360     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11361     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11362     this.bodyEl.setStyle("display", "block");
11363     this.bodyEl.setStyle("zoom", "1");
11364     this.hideAction();
11365
11366     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11367     /** @private */
11368     this.el = Roo.get(els.el, true);
11369     this.inner = Roo.get(els.inner, true);
11370     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11371     this.pnode = Roo.get(els.el.parentNode, true);
11372     this.el.on("mousedown", this.onTabMouseDown, this);
11373     this.el.on("click", this.onTabClick, this);
11374     /** @private */
11375     if(closable){
11376         var c = Roo.get(els.close, true);
11377         c.dom.title = this.closeText;
11378         c.addClassOnOver("close-over");
11379         c.on("click", this.closeClick, this);
11380      }
11381
11382     this.addEvents({
11383          /**
11384          * @event activate
11385          * Fires when this tab becomes the active tab.
11386          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11387          * @param {Roo.TabPanelItem} this
11388          */
11389         "activate": true,
11390         /**
11391          * @event beforeclose
11392          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11393          * @param {Roo.TabPanelItem} this
11394          * @param {Object} e Set cancel to true on this object to cancel the close.
11395          */
11396         "beforeclose": true,
11397         /**
11398          * @event close
11399          * Fires when this tab is closed.
11400          * @param {Roo.TabPanelItem} this
11401          */
11402          "close": true,
11403         /**
11404          * @event deactivate
11405          * Fires when this tab is no longer the active tab.
11406          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11407          * @param {Roo.TabPanelItem} this
11408          */
11409          "deactivate" : true
11410     });
11411     this.hidden = false;
11412
11413     Roo.TabPanelItem.superclass.constructor.call(this);
11414 };
11415
11416 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11417     purgeListeners : function(){
11418        Roo.util.Observable.prototype.purgeListeners.call(this);
11419        this.el.removeAllListeners();
11420     },
11421     /**
11422      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11423      */
11424     show : function(){
11425         this.pnode.addClass("on");
11426         this.showAction();
11427         if(Roo.isOpera){
11428             this.tabPanel.stripWrap.repaint();
11429         }
11430         this.fireEvent("activate", this.tabPanel, this);
11431     },
11432
11433     /**
11434      * Returns true if this tab is the active tab.
11435      * @return {Boolean}
11436      */
11437     isActive : function(){
11438         return this.tabPanel.getActiveTab() == this;
11439     },
11440
11441     /**
11442      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11443      */
11444     hide : function(){
11445         this.pnode.removeClass("on");
11446         this.hideAction();
11447         this.fireEvent("deactivate", this.tabPanel, this);
11448     },
11449
11450     hideAction : function(){
11451         this.bodyEl.hide();
11452         this.bodyEl.setStyle("position", "absolute");
11453         this.bodyEl.setLeft("-20000px");
11454         this.bodyEl.setTop("-20000px");
11455     },
11456
11457     showAction : function(){
11458         this.bodyEl.setStyle("position", "relative");
11459         this.bodyEl.setTop("");
11460         this.bodyEl.setLeft("");
11461         this.bodyEl.show();
11462     },
11463
11464     /**
11465      * Set the tooltip for the tab.
11466      * @param {String} tooltip The tab's tooltip
11467      */
11468     setTooltip : function(text){
11469         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11470             this.textEl.dom.qtip = text;
11471             this.textEl.dom.removeAttribute('title');
11472         }else{
11473             this.textEl.dom.title = text;
11474         }
11475     },
11476
11477     onTabClick : function(e){
11478         e.preventDefault();
11479         this.tabPanel.activate(this.id);
11480     },
11481
11482     onTabMouseDown : function(e){
11483         e.preventDefault();
11484         this.tabPanel.activate(this.id);
11485     },
11486
11487     getWidth : function(){
11488         return this.inner.getWidth();
11489     },
11490
11491     setWidth : function(width){
11492         var iwidth = width - this.pnode.getPadding("lr");
11493         this.inner.setWidth(iwidth);
11494         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11495         this.pnode.setWidth(width);
11496     },
11497
11498     /**
11499      * Show or hide the tab
11500      * @param {Boolean} hidden True to hide or false to show.
11501      */
11502     setHidden : function(hidden){
11503         this.hidden = hidden;
11504         this.pnode.setStyle("display", hidden ? "none" : "");
11505     },
11506
11507     /**
11508      * Returns true if this tab is "hidden"
11509      * @return {Boolean}
11510      */
11511     isHidden : function(){
11512         return this.hidden;
11513     },
11514
11515     /**
11516      * Returns the text for this tab
11517      * @return {String}
11518      */
11519     getText : function(){
11520         return this.text;
11521     },
11522
11523     autoSize : function(){
11524         //this.el.beginMeasure();
11525         this.textEl.setWidth(1);
11526         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11527         //this.el.endMeasure();
11528     },
11529
11530     /**
11531      * Sets the text for the tab (Note: this also sets the tooltip text)
11532      * @param {String} text The tab's text and tooltip
11533      */
11534     setText : function(text){
11535         this.text = text;
11536         this.textEl.update(text);
11537         this.setTooltip(text);
11538         if(!this.tabPanel.resizeTabs){
11539             this.autoSize();
11540         }
11541     },
11542     /**
11543      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11544      */
11545     activate : function(){
11546         this.tabPanel.activate(this.id);
11547     },
11548
11549     /**
11550      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11551      */
11552     disable : function(){
11553         if(this.tabPanel.active != this){
11554             this.disabled = true;
11555             this.pnode.addClass("disabled");
11556         }
11557     },
11558
11559     /**
11560      * Enables this TabPanelItem if it was previously disabled.
11561      */
11562     enable : function(){
11563         this.disabled = false;
11564         this.pnode.removeClass("disabled");
11565     },
11566
11567     /**
11568      * Sets the content for this TabPanelItem.
11569      * @param {String} content The content
11570      * @param {Boolean} loadScripts true to look for and load scripts
11571      */
11572     setContent : function(content, loadScripts){
11573         this.bodyEl.update(content, loadScripts);
11574     },
11575
11576     /**
11577      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11578      * @return {Roo.UpdateManager} The UpdateManager
11579      */
11580     getUpdateManager : function(){
11581         return this.bodyEl.getUpdateManager();
11582     },
11583
11584     /**
11585      * Set a URL to be used to load the content for this TabPanelItem.
11586      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11587      * @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)
11588      * @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)
11589      * @return {Roo.UpdateManager} The UpdateManager
11590      */
11591     setUrl : function(url, params, loadOnce){
11592         if(this.refreshDelegate){
11593             this.un('activate', this.refreshDelegate);
11594         }
11595         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11596         this.on("activate", this.refreshDelegate);
11597         return this.bodyEl.getUpdateManager();
11598     },
11599
11600     /** @private */
11601     _handleRefresh : function(url, params, loadOnce){
11602         if(!loadOnce || !this.loaded){
11603             var updater = this.bodyEl.getUpdateManager();
11604             updater.update(url, params, this._setLoaded.createDelegate(this));
11605         }
11606     },
11607
11608     /**
11609      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11610      *   Will fail silently if the setUrl method has not been called.
11611      *   This does not activate the panel, just updates its content.
11612      */
11613     refresh : function(){
11614         if(this.refreshDelegate){
11615            this.loaded = false;
11616            this.refreshDelegate();
11617         }
11618     },
11619
11620     /** @private */
11621     _setLoaded : function(){
11622         this.loaded = true;
11623     },
11624
11625     /** @private */
11626     closeClick : function(e){
11627         var o = {};
11628         e.stopEvent();
11629         this.fireEvent("beforeclose", this, o);
11630         if(o.cancel !== true){
11631             this.tabPanel.removeTab(this.id);
11632         }
11633     },
11634     /**
11635      * The text displayed in the tooltip for the close icon.
11636      * @type String
11637      */
11638     closeText : "Close this tab"
11639 });
11640
11641 /** @private */
11642 Roo.TabPanel.prototype.createStrip = function(container){
11643     var strip = document.createElement("div");
11644     strip.className = "x-tabs-wrap";
11645     container.appendChild(strip);
11646     return strip;
11647 };
11648 /** @private */
11649 Roo.TabPanel.prototype.createStripList = function(strip){
11650     // div wrapper for retard IE
11651     // returns the "tr" element.
11652     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11653         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11654         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11655     return strip.firstChild.firstChild.firstChild.firstChild;
11656 };
11657 /** @private */
11658 Roo.TabPanel.prototype.createBody = function(container){
11659     var body = document.createElement("div");
11660     Roo.id(body, "tab-body");
11661     Roo.fly(body).addClass("x-tabs-body");
11662     container.appendChild(body);
11663     return body;
11664 };
11665 /** @private */
11666 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11667     var body = Roo.getDom(id);
11668     if(!body){
11669         body = document.createElement("div");
11670         body.id = id;
11671     }
11672     Roo.fly(body).addClass("x-tabs-item-body");
11673     bodyEl.insertBefore(body, bodyEl.firstChild);
11674     return body;
11675 };
11676 /** @private */
11677 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11678     var td = document.createElement("td");
11679     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11680     //stripEl.appendChild(td);
11681     if(closable){
11682         td.className = "x-tabs-closable";
11683         if(!this.closeTpl){
11684             this.closeTpl = new Roo.Template(
11685                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11686                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11687                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11688             );
11689         }
11690         var el = this.closeTpl.overwrite(td, {"text": text});
11691         var close = el.getElementsByTagName("div")[0];
11692         var inner = el.getElementsByTagName("em")[0];
11693         return {"el": el, "close": close, "inner": inner};
11694     } else {
11695         if(!this.tabTpl){
11696             this.tabTpl = new Roo.Template(
11697                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11698                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11699             );
11700         }
11701         var el = this.tabTpl.overwrite(td, {"text": text});
11702         var inner = el.getElementsByTagName("em")[0];
11703         return {"el": el, "inner": inner};
11704     }
11705 };/*
11706  * Based on:
11707  * Ext JS Library 1.1.1
11708  * Copyright(c) 2006-2007, Ext JS, LLC.
11709  *
11710  * Originally Released Under LGPL - original licence link has changed is not relivant.
11711  *
11712  * Fork - LGPL
11713  * <script type="text/javascript">
11714  */
11715
11716 /**
11717  * @class Roo.Button
11718  * @extends Roo.util.Observable
11719  * Simple Button class
11720  * @cfg {String} text The button text
11721  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11722  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11723  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11724  * @cfg {Object} scope The scope of the handler
11725  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11726  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11727  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11728  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11729  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11730  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11731    applies if enableToggle = true)
11732  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11733  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11734   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11735  * @constructor
11736  * Create a new button
11737  * @param {Object} config The config object
11738  */
11739 Roo.Button = function(renderTo, config)
11740 {
11741     if (!config) {
11742         config = renderTo;
11743         renderTo = config.renderTo || false;
11744     }
11745     
11746     Roo.apply(this, config);
11747     this.addEvents({
11748         /**
11749              * @event click
11750              * Fires when this button is clicked
11751              * @param {Button} this
11752              * @param {EventObject} e The click event
11753              */
11754             "click" : true,
11755         /**
11756              * @event toggle
11757              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11758              * @param {Button} this
11759              * @param {Boolean} pressed
11760              */
11761             "toggle" : true,
11762         /**
11763              * @event mouseover
11764              * Fires when the mouse hovers over the button
11765              * @param {Button} this
11766              * @param {Event} e The event object
11767              */
11768         'mouseover' : true,
11769         /**
11770              * @event mouseout
11771              * Fires when the mouse exits the button
11772              * @param {Button} this
11773              * @param {Event} e The event object
11774              */
11775         'mouseout': true,
11776          /**
11777              * @event render
11778              * Fires when the button is rendered
11779              * @param {Button} this
11780              */
11781         'render': true
11782     });
11783     if(this.menu){
11784         this.menu = Roo.menu.MenuMgr.get(this.menu);
11785     }
11786     // register listeners first!!  - so render can be captured..
11787     Roo.util.Observable.call(this);
11788     if(renderTo){
11789         this.render(renderTo);
11790     }
11791     
11792   
11793 };
11794
11795 Roo.extend(Roo.Button, Roo.util.Observable, {
11796     /**
11797      * 
11798      */
11799     
11800     /**
11801      * Read-only. True if this button is hidden
11802      * @type Boolean
11803      */
11804     hidden : false,
11805     /**
11806      * Read-only. True if this button is disabled
11807      * @type Boolean
11808      */
11809     disabled : false,
11810     /**
11811      * Read-only. True if this button is pressed (only if enableToggle = true)
11812      * @type Boolean
11813      */
11814     pressed : false,
11815
11816     /**
11817      * @cfg {Number} tabIndex 
11818      * The DOM tabIndex for this button (defaults to undefined)
11819      */
11820     tabIndex : undefined,
11821
11822     /**
11823      * @cfg {Boolean} enableToggle
11824      * True to enable pressed/not pressed toggling (defaults to false)
11825      */
11826     enableToggle: false,
11827     /**
11828      * @cfg {Mixed} menu
11829      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11830      */
11831     menu : undefined,
11832     /**
11833      * @cfg {String} menuAlign
11834      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11835      */
11836     menuAlign : "tl-bl?",
11837
11838     /**
11839      * @cfg {String} iconCls
11840      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11841      */
11842     iconCls : undefined,
11843     /**
11844      * @cfg {String} type
11845      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11846      */
11847     type : 'button',
11848
11849     // private
11850     menuClassTarget: 'tr',
11851
11852     /**
11853      * @cfg {String} clickEvent
11854      * The type of event to map to the button's event handler (defaults to 'click')
11855      */
11856     clickEvent : 'click',
11857
11858     /**
11859      * @cfg {Boolean} handleMouseEvents
11860      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11861      */
11862     handleMouseEvents : true,
11863
11864     /**
11865      * @cfg {String} tooltipType
11866      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11867      */
11868     tooltipType : 'qtip',
11869
11870     /**
11871      * @cfg {String} cls
11872      * A CSS class to apply to the button's main element.
11873      */
11874     
11875     /**
11876      * @cfg {Roo.Template} template (Optional)
11877      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11878      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11879      * require code modifications if required elements (e.g. a button) aren't present.
11880      */
11881
11882     // private
11883     render : function(renderTo){
11884         var btn;
11885         if(this.hideParent){
11886             this.parentEl = Roo.get(renderTo);
11887         }
11888         if(!this.dhconfig){
11889             if(!this.template){
11890                 if(!Roo.Button.buttonTemplate){
11891                     // hideous table template
11892                     Roo.Button.buttonTemplate = new Roo.Template(
11893                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11894                         '<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>',
11895                         "</tr></tbody></table>");
11896                 }
11897                 this.template = Roo.Button.buttonTemplate;
11898             }
11899             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11900             var btnEl = btn.child("button:first");
11901             btnEl.on('focus', this.onFocus, this);
11902             btnEl.on('blur', this.onBlur, this);
11903             if(this.cls){
11904                 btn.addClass(this.cls);
11905             }
11906             if(this.icon){
11907                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11908             }
11909             if(this.iconCls){
11910                 btnEl.addClass(this.iconCls);
11911                 if(!this.cls){
11912                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11913                 }
11914             }
11915             if(this.tabIndex !== undefined){
11916                 btnEl.dom.tabIndex = this.tabIndex;
11917             }
11918             if(this.tooltip){
11919                 if(typeof this.tooltip == 'object'){
11920                     Roo.QuickTips.tips(Roo.apply({
11921                           target: btnEl.id
11922                     }, this.tooltip));
11923                 } else {
11924                     btnEl.dom[this.tooltipType] = this.tooltip;
11925                 }
11926             }
11927         }else{
11928             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11929         }
11930         this.el = btn;
11931         if(this.id){
11932             this.el.dom.id = this.el.id = this.id;
11933         }
11934         if(this.menu){
11935             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11936             this.menu.on("show", this.onMenuShow, this);
11937             this.menu.on("hide", this.onMenuHide, this);
11938         }
11939         btn.addClass("x-btn");
11940         if(Roo.isIE && !Roo.isIE7){
11941             this.autoWidth.defer(1, this);
11942         }else{
11943             this.autoWidth();
11944         }
11945         if(this.handleMouseEvents){
11946             btn.on("mouseover", this.onMouseOver, this);
11947             btn.on("mouseout", this.onMouseOut, this);
11948             btn.on("mousedown", this.onMouseDown, this);
11949         }
11950         btn.on(this.clickEvent, this.onClick, this);
11951         //btn.on("mouseup", this.onMouseUp, this);
11952         if(this.hidden){
11953             this.hide();
11954         }
11955         if(this.disabled){
11956             this.disable();
11957         }
11958         Roo.ButtonToggleMgr.register(this);
11959         if(this.pressed){
11960             this.el.addClass("x-btn-pressed");
11961         }
11962         if(this.repeat){
11963             var repeater = new Roo.util.ClickRepeater(btn,
11964                 typeof this.repeat == "object" ? this.repeat : {}
11965             );
11966             repeater.on("click", this.onClick,  this);
11967         }
11968         
11969         this.fireEvent('render', this);
11970         
11971     },
11972     /**
11973      * Returns the button's underlying element
11974      * @return {Roo.Element} The element
11975      */
11976     getEl : function(){
11977         return this.el;  
11978     },
11979     
11980     /**
11981      * Destroys this Button and removes any listeners.
11982      */
11983     destroy : function(){
11984         Roo.ButtonToggleMgr.unregister(this);
11985         this.el.removeAllListeners();
11986         this.purgeListeners();
11987         this.el.remove();
11988     },
11989
11990     // private
11991     autoWidth : function(){
11992         if(this.el){
11993             this.el.setWidth("auto");
11994             if(Roo.isIE7 && Roo.isStrict){
11995                 var ib = this.el.child('button');
11996                 if(ib && ib.getWidth() > 20){
11997                     ib.clip();
11998                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11999                 }
12000             }
12001             if(this.minWidth){
12002                 if(this.hidden){
12003                     this.el.beginMeasure();
12004                 }
12005                 if(this.el.getWidth() < this.minWidth){
12006                     this.el.setWidth(this.minWidth);
12007                 }
12008                 if(this.hidden){
12009                     this.el.endMeasure();
12010                 }
12011             }
12012         }
12013     },
12014
12015     /**
12016      * Assigns this button's click handler
12017      * @param {Function} handler The function to call when the button is clicked
12018      * @param {Object} scope (optional) Scope for the function passed in
12019      */
12020     setHandler : function(handler, scope){
12021         this.handler = handler;
12022         this.scope = scope;  
12023     },
12024     
12025     /**
12026      * Sets this button's text
12027      * @param {String} text The button text
12028      */
12029     setText : function(text){
12030         this.text = text;
12031         if(this.el){
12032             this.el.child("td.x-btn-center button.x-btn-text").update(text);
12033         }
12034         this.autoWidth();
12035     },
12036     
12037     /**
12038      * Gets the text for this button
12039      * @return {String} The button text
12040      */
12041     getText : function(){
12042         return this.text;  
12043     },
12044     
12045     /**
12046      * Show this button
12047      */
12048     show: function(){
12049         this.hidden = false;
12050         if(this.el){
12051             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
12052         }
12053     },
12054     
12055     /**
12056      * Hide this button
12057      */
12058     hide: function(){
12059         this.hidden = true;
12060         if(this.el){
12061             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
12062         }
12063     },
12064     
12065     /**
12066      * Convenience function for boolean show/hide
12067      * @param {Boolean} visible True to show, false to hide
12068      */
12069     setVisible: function(visible){
12070         if(visible) {
12071             this.show();
12072         }else{
12073             this.hide();
12074         }
12075     },
12076     
12077     /**
12078      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
12079      * @param {Boolean} state (optional) Force a particular state
12080      */
12081     toggle : function(state){
12082         state = state === undefined ? !this.pressed : state;
12083         if(state != this.pressed){
12084             if(state){
12085                 this.el.addClass("x-btn-pressed");
12086                 this.pressed = true;
12087                 this.fireEvent("toggle", this, true);
12088             }else{
12089                 this.el.removeClass("x-btn-pressed");
12090                 this.pressed = false;
12091                 this.fireEvent("toggle", this, false);
12092             }
12093             if(this.toggleHandler){
12094                 this.toggleHandler.call(this.scope || this, this, state);
12095             }
12096         }
12097     },
12098     
12099     /**
12100      * Focus the button
12101      */
12102     focus : function(){
12103         this.el.child('button:first').focus();
12104     },
12105     
12106     /**
12107      * Disable this button
12108      */
12109     disable : function(){
12110         if(this.el){
12111             this.el.addClass("x-btn-disabled");
12112         }
12113         this.disabled = true;
12114     },
12115     
12116     /**
12117      * Enable this button
12118      */
12119     enable : function(){
12120         if(this.el){
12121             this.el.removeClass("x-btn-disabled");
12122         }
12123         this.disabled = false;
12124     },
12125
12126     /**
12127      * Convenience function for boolean enable/disable
12128      * @param {Boolean} enabled True to enable, false to disable
12129      */
12130     setDisabled : function(v){
12131         this[v !== true ? "enable" : "disable"]();
12132     },
12133
12134     // private
12135     onClick : function(e){
12136         if(e){
12137             e.preventDefault();
12138         }
12139         if(e.button != 0){
12140             return;
12141         }
12142         if(!this.disabled){
12143             if(this.enableToggle){
12144                 this.toggle();
12145             }
12146             if(this.menu && !this.menu.isVisible()){
12147                 this.menu.show(this.el, this.menuAlign);
12148             }
12149             this.fireEvent("click", this, e);
12150             if(this.handler){
12151                 this.el.removeClass("x-btn-over");
12152                 this.handler.call(this.scope || this, this, e);
12153             }
12154         }
12155     },
12156     // private
12157     onMouseOver : function(e){
12158         if(!this.disabled){
12159             this.el.addClass("x-btn-over");
12160             this.fireEvent('mouseover', this, e);
12161         }
12162     },
12163     // private
12164     onMouseOut : function(e){
12165         if(!e.within(this.el,  true)){
12166             this.el.removeClass("x-btn-over");
12167             this.fireEvent('mouseout', this, e);
12168         }
12169     },
12170     // private
12171     onFocus : function(e){
12172         if(!this.disabled){
12173             this.el.addClass("x-btn-focus");
12174         }
12175     },
12176     // private
12177     onBlur : function(e){
12178         this.el.removeClass("x-btn-focus");
12179     },
12180     // private
12181     onMouseDown : function(e){
12182         if(!this.disabled && e.button == 0){
12183             this.el.addClass("x-btn-click");
12184             Roo.get(document).on('mouseup', this.onMouseUp, this);
12185         }
12186     },
12187     // private
12188     onMouseUp : function(e){
12189         if(e.button == 0){
12190             this.el.removeClass("x-btn-click");
12191             Roo.get(document).un('mouseup', this.onMouseUp, this);
12192         }
12193     },
12194     // private
12195     onMenuShow : function(e){
12196         this.el.addClass("x-btn-menu-active");
12197     },
12198     // private
12199     onMenuHide : function(e){
12200         this.el.removeClass("x-btn-menu-active");
12201     }   
12202 });
12203
12204 // Private utility class used by Button
12205 Roo.ButtonToggleMgr = function(){
12206    var groups = {};
12207    
12208    function toggleGroup(btn, state){
12209        if(state){
12210            var g = groups[btn.toggleGroup];
12211            for(var i = 0, l = g.length; i < l; i++){
12212                if(g[i] != btn){
12213                    g[i].toggle(false);
12214                }
12215            }
12216        }
12217    }
12218    
12219    return {
12220        register : function(btn){
12221            if(!btn.toggleGroup){
12222                return;
12223            }
12224            var g = groups[btn.toggleGroup];
12225            if(!g){
12226                g = groups[btn.toggleGroup] = [];
12227            }
12228            g.push(btn);
12229            btn.on("toggle", toggleGroup);
12230        },
12231        
12232        unregister : function(btn){
12233            if(!btn.toggleGroup){
12234                return;
12235            }
12236            var g = groups[btn.toggleGroup];
12237            if(g){
12238                g.remove(btn);
12239                btn.un("toggle", toggleGroup);
12240            }
12241        }
12242    };
12243 }();/*
12244  * Based on:
12245  * Ext JS Library 1.1.1
12246  * Copyright(c) 2006-2007, Ext JS, LLC.
12247  *
12248  * Originally Released Under LGPL - original licence link has changed is not relivant.
12249  *
12250  * Fork - LGPL
12251  * <script type="text/javascript">
12252  */
12253  
12254 /**
12255  * @class Roo.SplitButton
12256  * @extends Roo.Button
12257  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12258  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12259  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12260  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12261  * @cfg {String} arrowTooltip The title attribute of the arrow
12262  * @constructor
12263  * Create a new menu button
12264  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12265  * @param {Object} config The config object
12266  */
12267 Roo.SplitButton = function(renderTo, config){
12268     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12269     /**
12270      * @event arrowclick
12271      * Fires when this button's arrow is clicked
12272      * @param {SplitButton} this
12273      * @param {EventObject} e The click event
12274      */
12275     this.addEvents({"arrowclick":true});
12276 };
12277
12278 Roo.extend(Roo.SplitButton, Roo.Button, {
12279     render : function(renderTo){
12280         // this is one sweet looking template!
12281         var tpl = new Roo.Template(
12282             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12283             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12284             '<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>',
12285             "</tbody></table></td><td>",
12286             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12287             '<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>',
12288             "</tbody></table></td></tr></table>"
12289         );
12290         var btn = tpl.append(renderTo, [this.text, this.type], true);
12291         var btnEl = btn.child("button");
12292         if(this.cls){
12293             btn.addClass(this.cls);
12294         }
12295         if(this.icon){
12296             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12297         }
12298         if(this.iconCls){
12299             btnEl.addClass(this.iconCls);
12300             if(!this.cls){
12301                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12302             }
12303         }
12304         this.el = btn;
12305         if(this.handleMouseEvents){
12306             btn.on("mouseover", this.onMouseOver, this);
12307             btn.on("mouseout", this.onMouseOut, this);
12308             btn.on("mousedown", this.onMouseDown, this);
12309             btn.on("mouseup", this.onMouseUp, this);
12310         }
12311         btn.on(this.clickEvent, this.onClick, this);
12312         if(this.tooltip){
12313             if(typeof this.tooltip == 'object'){
12314                 Roo.QuickTips.tips(Roo.apply({
12315                       target: btnEl.id
12316                 }, this.tooltip));
12317             } else {
12318                 btnEl.dom[this.tooltipType] = this.tooltip;
12319             }
12320         }
12321         if(this.arrowTooltip){
12322             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12323         }
12324         if(this.hidden){
12325             this.hide();
12326         }
12327         if(this.disabled){
12328             this.disable();
12329         }
12330         if(this.pressed){
12331             this.el.addClass("x-btn-pressed");
12332         }
12333         if(Roo.isIE && !Roo.isIE7){
12334             this.autoWidth.defer(1, this);
12335         }else{
12336             this.autoWidth();
12337         }
12338         if(this.menu){
12339             this.menu.on("show", this.onMenuShow, this);
12340             this.menu.on("hide", this.onMenuHide, this);
12341         }
12342         this.fireEvent('render', this);
12343     },
12344
12345     // private
12346     autoWidth : function(){
12347         if(this.el){
12348             var tbl = this.el.child("table:first");
12349             var tbl2 = this.el.child("table:last");
12350             this.el.setWidth("auto");
12351             tbl.setWidth("auto");
12352             if(Roo.isIE7 && Roo.isStrict){
12353                 var ib = this.el.child('button:first');
12354                 if(ib && ib.getWidth() > 20){
12355                     ib.clip();
12356                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12357                 }
12358             }
12359             if(this.minWidth){
12360                 if(this.hidden){
12361                     this.el.beginMeasure();
12362                 }
12363                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12364                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12365                 }
12366                 if(this.hidden){
12367                     this.el.endMeasure();
12368                 }
12369             }
12370             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12371         } 
12372     },
12373     /**
12374      * Sets this button's click handler
12375      * @param {Function} handler The function to call when the button is clicked
12376      * @param {Object} scope (optional) Scope for the function passed above
12377      */
12378     setHandler : function(handler, scope){
12379         this.handler = handler;
12380         this.scope = scope;  
12381     },
12382     
12383     /**
12384      * Sets this button's arrow click handler
12385      * @param {Function} handler The function to call when the arrow is clicked
12386      * @param {Object} scope (optional) Scope for the function passed above
12387      */
12388     setArrowHandler : function(handler, scope){
12389         this.arrowHandler = handler;
12390         this.scope = scope;  
12391     },
12392     
12393     /**
12394      * Focus the button
12395      */
12396     focus : function(){
12397         if(this.el){
12398             this.el.child("button:first").focus();
12399         }
12400     },
12401
12402     // private
12403     onClick : function(e){
12404         e.preventDefault();
12405         if(!this.disabled){
12406             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12407                 if(this.menu && !this.menu.isVisible()){
12408                     this.menu.show(this.el, this.menuAlign);
12409                 }
12410                 this.fireEvent("arrowclick", this, e);
12411                 if(this.arrowHandler){
12412                     this.arrowHandler.call(this.scope || this, this, e);
12413                 }
12414             }else{
12415                 this.fireEvent("click", this, e);
12416                 if(this.handler){
12417                     this.handler.call(this.scope || this, this, e);
12418                 }
12419             }
12420         }
12421     },
12422     // private
12423     onMouseDown : function(e){
12424         if(!this.disabled){
12425             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12426         }
12427     },
12428     // private
12429     onMouseUp : function(e){
12430         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12431     }   
12432 });
12433
12434
12435 // backwards compat
12436 Roo.MenuButton = Roo.SplitButton;/*
12437  * Based on:
12438  * Ext JS Library 1.1.1
12439  * Copyright(c) 2006-2007, Ext JS, LLC.
12440  *
12441  * Originally Released Under LGPL - original licence link has changed is not relivant.
12442  *
12443  * Fork - LGPL
12444  * <script type="text/javascript">
12445  */
12446
12447 /**
12448  * @class Roo.Toolbar
12449  * Basic Toolbar class.
12450  * @constructor
12451  * Creates a new Toolbar
12452  * @param {Object} container The config object
12453  */ 
12454 Roo.Toolbar = function(container, buttons, config)
12455 {
12456     /// old consturctor format still supported..
12457     if(container instanceof Array){ // omit the container for later rendering
12458         buttons = container;
12459         config = buttons;
12460         container = null;
12461     }
12462     if (typeof(container) == 'object' && container.xtype) {
12463         config = container;
12464         container = config.container;
12465         buttons = config.buttons || []; // not really - use items!!
12466     }
12467     var xitems = [];
12468     if (config && config.items) {
12469         xitems = config.items;
12470         delete config.items;
12471     }
12472     Roo.apply(this, config);
12473     this.buttons = buttons;
12474     
12475     if(container){
12476         this.render(container);
12477     }
12478     this.xitems = xitems;
12479     Roo.each(xitems, function(b) {
12480         this.add(b);
12481     }, this);
12482     
12483 };
12484
12485 Roo.Toolbar.prototype = {
12486     /**
12487      * @cfg {Array} items
12488      * array of button configs or elements to add (will be converted to a MixedCollection)
12489      */
12490     
12491     /**
12492      * @cfg {String/HTMLElement/Element} container
12493      * The id or element that will contain the toolbar
12494      */
12495     // private
12496     render : function(ct){
12497         this.el = Roo.get(ct);
12498         if(this.cls){
12499             this.el.addClass(this.cls);
12500         }
12501         // using a table allows for vertical alignment
12502         // 100% width is needed by Safari...
12503         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12504         this.tr = this.el.child("tr", true);
12505         var autoId = 0;
12506         this.items = new Roo.util.MixedCollection(false, function(o){
12507             return o.id || ("item" + (++autoId));
12508         });
12509         if(this.buttons){
12510             this.add.apply(this, this.buttons);
12511             delete this.buttons;
12512         }
12513     },
12514
12515     /**
12516      * Adds element(s) to the toolbar -- this function takes a variable number of 
12517      * arguments of mixed type and adds them to the toolbar.
12518      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12519      * <ul>
12520      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12521      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12522      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12523      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12524      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12525      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12526      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12527      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12528      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12529      * </ul>
12530      * @param {Mixed} arg2
12531      * @param {Mixed} etc.
12532      */
12533     add : function(){
12534         var a = arguments, l = a.length;
12535         for(var i = 0; i < l; i++){
12536             this._add(a[i]);
12537         }
12538     },
12539     // private..
12540     _add : function(el) {
12541         
12542         if (el.xtype) {
12543             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12544         }
12545         
12546         if (el.applyTo){ // some kind of form field
12547             return this.addField(el);
12548         } 
12549         if (el.render){ // some kind of Toolbar.Item
12550             return this.addItem(el);
12551         }
12552         if (typeof el == "string"){ // string
12553             if(el == "separator" || el == "-"){
12554                 return this.addSeparator();
12555             }
12556             if (el == " "){
12557                 return this.addSpacer();
12558             }
12559             if(el == "->"){
12560                 return this.addFill();
12561             }
12562             return this.addText(el);
12563             
12564         }
12565         if(el.tagName){ // element
12566             return this.addElement(el);
12567         }
12568         if(typeof el == "object"){ // must be button config?
12569             return this.addButton(el);
12570         }
12571         // and now what?!?!
12572         return false;
12573         
12574     },
12575     
12576     /**
12577      * Add an Xtype element
12578      * @param {Object} xtype Xtype Object
12579      * @return {Object} created Object
12580      */
12581     addxtype : function(e){
12582         return this.add(e);  
12583     },
12584     
12585     /**
12586      * Returns the Element for this toolbar.
12587      * @return {Roo.Element}
12588      */
12589     getEl : function(){
12590         return this.el;  
12591     },
12592     
12593     /**
12594      * Adds a separator
12595      * @return {Roo.Toolbar.Item} The separator item
12596      */
12597     addSeparator : function(){
12598         return this.addItem(new Roo.Toolbar.Separator());
12599     },
12600
12601     /**
12602      * Adds a spacer element
12603      * @return {Roo.Toolbar.Spacer} The spacer item
12604      */
12605     addSpacer : function(){
12606         return this.addItem(new Roo.Toolbar.Spacer());
12607     },
12608
12609     /**
12610      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12611      * @return {Roo.Toolbar.Fill} The fill item
12612      */
12613     addFill : function(){
12614         return this.addItem(new Roo.Toolbar.Fill());
12615     },
12616
12617     /**
12618      * Adds any standard HTML element to the toolbar
12619      * @param {String/HTMLElement/Element} el The element or id of the element to add
12620      * @return {Roo.Toolbar.Item} The element's item
12621      */
12622     addElement : function(el){
12623         return this.addItem(new Roo.Toolbar.Item(el));
12624     },
12625     /**
12626      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12627      * @type Roo.util.MixedCollection  
12628      */
12629     items : false,
12630      
12631     /**
12632      * Adds any Toolbar.Item or subclass
12633      * @param {Roo.Toolbar.Item} item
12634      * @return {Roo.Toolbar.Item} The item
12635      */
12636     addItem : function(item){
12637         var td = this.nextBlock();
12638         item.render(td);
12639         this.items.add(item);
12640         return item;
12641     },
12642     
12643     /**
12644      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12645      * @param {Object/Array} config A button config or array of configs
12646      * @return {Roo.Toolbar.Button/Array}
12647      */
12648     addButton : function(config){
12649         if(config instanceof Array){
12650             var buttons = [];
12651             for(var i = 0, len = config.length; i < len; i++) {
12652                 buttons.push(this.addButton(config[i]));
12653             }
12654             return buttons;
12655         }
12656         var b = config;
12657         if(!(config instanceof Roo.Toolbar.Button)){
12658             b = config.split ?
12659                 new Roo.Toolbar.SplitButton(config) :
12660                 new Roo.Toolbar.Button(config);
12661         }
12662         var td = this.nextBlock();
12663         b.render(td);
12664         this.items.add(b);
12665         return b;
12666     },
12667     
12668     /**
12669      * Adds text to the toolbar
12670      * @param {String} text The text to add
12671      * @return {Roo.Toolbar.Item} The element's item
12672      */
12673     addText : function(text){
12674         return this.addItem(new Roo.Toolbar.TextItem(text));
12675     },
12676     
12677     /**
12678      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12679      * @param {Number} index The index where the item is to be inserted
12680      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12681      * @return {Roo.Toolbar.Button/Item}
12682      */
12683     insertButton : function(index, item){
12684         if(item instanceof Array){
12685             var buttons = [];
12686             for(var i = 0, len = item.length; i < len; i++) {
12687                buttons.push(this.insertButton(index + i, item[i]));
12688             }
12689             return buttons;
12690         }
12691         if (!(item instanceof Roo.Toolbar.Button)){
12692            item = new Roo.Toolbar.Button(item);
12693         }
12694         var td = document.createElement("td");
12695         this.tr.insertBefore(td, this.tr.childNodes[index]);
12696         item.render(td);
12697         this.items.insert(index, item);
12698         return item;
12699     },
12700     
12701     /**
12702      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12703      * @param {Object} config
12704      * @return {Roo.Toolbar.Item} The element's item
12705      */
12706     addDom : function(config, returnEl){
12707         var td = this.nextBlock();
12708         Roo.DomHelper.overwrite(td, config);
12709         var ti = new Roo.Toolbar.Item(td.firstChild);
12710         ti.render(td);
12711         this.items.add(ti);
12712         return ti;
12713     },
12714
12715     /**
12716      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12717      * @type Roo.util.MixedCollection  
12718      */
12719     fields : false,
12720     
12721     /**
12722      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12723      * Note: the field should not have been rendered yet. For a field that has already been
12724      * rendered, use {@link #addElement}.
12725      * @param {Roo.form.Field} field
12726      * @return {Roo.ToolbarItem}
12727      */
12728      
12729       
12730     addField : function(field) {
12731         if (!this.fields) {
12732             var autoId = 0;
12733             this.fields = new Roo.util.MixedCollection(false, function(o){
12734                 return o.id || ("item" + (++autoId));
12735             });
12736
12737         }
12738         
12739         var td = this.nextBlock();
12740         field.render(td);
12741         var ti = new Roo.Toolbar.Item(td.firstChild);
12742         ti.render(td);
12743         this.items.add(ti);
12744         this.fields.add(field);
12745         return ti;
12746     },
12747     /**
12748      * Hide the toolbar
12749      * @method hide
12750      */
12751      
12752       
12753     hide : function()
12754     {
12755         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12756         this.el.child('div').hide();
12757     },
12758     /**
12759      * Show the toolbar
12760      * @method show
12761      */
12762     show : function()
12763     {
12764         this.el.child('div').show();
12765     },
12766       
12767     // private
12768     nextBlock : function(){
12769         var td = document.createElement("td");
12770         this.tr.appendChild(td);
12771         return td;
12772     },
12773
12774     // private
12775     destroy : function(){
12776         if(this.items){ // rendered?
12777             Roo.destroy.apply(Roo, this.items.items);
12778         }
12779         if(this.fields){ // rendered?
12780             Roo.destroy.apply(Roo, this.fields.items);
12781         }
12782         Roo.Element.uncache(this.el, this.tr);
12783     }
12784 };
12785
12786 /**
12787  * @class Roo.Toolbar.Item
12788  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12789  * @constructor
12790  * Creates a new Item
12791  * @param {HTMLElement} el 
12792  */
12793 Roo.Toolbar.Item = function(el){
12794     this.el = Roo.getDom(el);
12795     this.id = Roo.id(this.el);
12796     this.hidden = false;
12797 };
12798
12799 Roo.Toolbar.Item.prototype = {
12800     
12801     /**
12802      * Get this item's HTML Element
12803      * @return {HTMLElement}
12804      */
12805     getEl : function(){
12806        return this.el;  
12807     },
12808
12809     // private
12810     render : function(td){
12811         this.td = td;
12812         td.appendChild(this.el);
12813     },
12814     
12815     /**
12816      * Removes and destroys this item.
12817      */
12818     destroy : function(){
12819         this.td.parentNode.removeChild(this.td);
12820     },
12821     
12822     /**
12823      * Shows this item.
12824      */
12825     show: function(){
12826         this.hidden = false;
12827         this.td.style.display = "";
12828     },
12829     
12830     /**
12831      * Hides this item.
12832      */
12833     hide: function(){
12834         this.hidden = true;
12835         this.td.style.display = "none";
12836     },
12837     
12838     /**
12839      * Convenience function for boolean show/hide.
12840      * @param {Boolean} visible true to show/false to hide
12841      */
12842     setVisible: function(visible){
12843         if(visible) {
12844             this.show();
12845         }else{
12846             this.hide();
12847         }
12848     },
12849     
12850     /**
12851      * Try to focus this item.
12852      */
12853     focus : function(){
12854         Roo.fly(this.el).focus();
12855     },
12856     
12857     /**
12858      * Disables this item.
12859      */
12860     disable : function(){
12861         Roo.fly(this.td).addClass("x-item-disabled");
12862         this.disabled = true;
12863         this.el.disabled = true;
12864     },
12865     
12866     /**
12867      * Enables this item.
12868      */
12869     enable : function(){
12870         Roo.fly(this.td).removeClass("x-item-disabled");
12871         this.disabled = false;
12872         this.el.disabled = false;
12873     }
12874 };
12875
12876
12877 /**
12878  * @class Roo.Toolbar.Separator
12879  * @extends Roo.Toolbar.Item
12880  * A simple toolbar separator class
12881  * @constructor
12882  * Creates a new Separator
12883  */
12884 Roo.Toolbar.Separator = function(){
12885     var s = document.createElement("span");
12886     s.className = "ytb-sep";
12887     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12888 };
12889 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12890     enable:Roo.emptyFn,
12891     disable:Roo.emptyFn,
12892     focus:Roo.emptyFn
12893 });
12894
12895 /**
12896  * @class Roo.Toolbar.Spacer
12897  * @extends Roo.Toolbar.Item
12898  * A simple element that adds extra horizontal space to a toolbar.
12899  * @constructor
12900  * Creates a new Spacer
12901  */
12902 Roo.Toolbar.Spacer = function(){
12903     var s = document.createElement("div");
12904     s.className = "ytb-spacer";
12905     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12906 };
12907 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12908     enable:Roo.emptyFn,
12909     disable:Roo.emptyFn,
12910     focus:Roo.emptyFn
12911 });
12912
12913 /**
12914  * @class Roo.Toolbar.Fill
12915  * @extends Roo.Toolbar.Spacer
12916  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12917  * @constructor
12918  * Creates a new Spacer
12919  */
12920 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12921     // private
12922     render : function(td){
12923         td.style.width = '100%';
12924         Roo.Toolbar.Fill.superclass.render.call(this, td);
12925     }
12926 });
12927
12928 /**
12929  * @class Roo.Toolbar.TextItem
12930  * @extends Roo.Toolbar.Item
12931  * A simple class that renders text directly into a toolbar.
12932  * @constructor
12933  * Creates a new TextItem
12934  * @param {String} text
12935  */
12936 Roo.Toolbar.TextItem = function(text){
12937     if (typeof(text) == 'object') {
12938         text = text.text;
12939     }
12940     var s = document.createElement("span");
12941     s.className = "ytb-text";
12942     s.innerHTML = text;
12943     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12944 };
12945 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12946     enable:Roo.emptyFn,
12947     disable:Roo.emptyFn,
12948     focus:Roo.emptyFn
12949 });
12950
12951 /**
12952  * @class Roo.Toolbar.Button
12953  * @extends Roo.Button
12954  * A button that renders into a toolbar.
12955  * @constructor
12956  * Creates a new Button
12957  * @param {Object} config A standard {@link Roo.Button} config object
12958  */
12959 Roo.Toolbar.Button = function(config){
12960     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12961 };
12962 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12963     render : function(td){
12964         this.td = td;
12965         Roo.Toolbar.Button.superclass.render.call(this, td);
12966     },
12967     
12968     /**
12969      * Removes and destroys this button
12970      */
12971     destroy : function(){
12972         Roo.Toolbar.Button.superclass.destroy.call(this);
12973         this.td.parentNode.removeChild(this.td);
12974     },
12975     
12976     /**
12977      * Shows this button
12978      */
12979     show: function(){
12980         this.hidden = false;
12981         this.td.style.display = "";
12982     },
12983     
12984     /**
12985      * Hides this button
12986      */
12987     hide: function(){
12988         this.hidden = true;
12989         this.td.style.display = "none";
12990     },
12991
12992     /**
12993      * Disables this item
12994      */
12995     disable : function(){
12996         Roo.fly(this.td).addClass("x-item-disabled");
12997         this.disabled = true;
12998     },
12999
13000     /**
13001      * Enables this item
13002      */
13003     enable : function(){
13004         Roo.fly(this.td).removeClass("x-item-disabled");
13005         this.disabled = false;
13006     }
13007 });
13008 // backwards compat
13009 Roo.ToolbarButton = Roo.Toolbar.Button;
13010
13011 /**
13012  * @class Roo.Toolbar.SplitButton
13013  * @extends Roo.SplitButton
13014  * A menu button that renders into a toolbar.
13015  * @constructor
13016  * Creates a new SplitButton
13017  * @param {Object} config A standard {@link Roo.SplitButton} config object
13018  */
13019 Roo.Toolbar.SplitButton = function(config){
13020     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
13021 };
13022 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
13023     render : function(td){
13024         this.td = td;
13025         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
13026     },
13027     
13028     /**
13029      * Removes and destroys this button
13030      */
13031     destroy : function(){
13032         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
13033         this.td.parentNode.removeChild(this.td);
13034     },
13035     
13036     /**
13037      * Shows this button
13038      */
13039     show: function(){
13040         this.hidden = false;
13041         this.td.style.display = "";
13042     },
13043     
13044     /**
13045      * Hides this button
13046      */
13047     hide: function(){
13048         this.hidden = true;
13049         this.td.style.display = "none";
13050     }
13051 });
13052
13053 // backwards compat
13054 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
13055  * Based on:
13056  * Ext JS Library 1.1.1
13057  * Copyright(c) 2006-2007, Ext JS, LLC.
13058  *
13059  * Originally Released Under LGPL - original licence link has changed is not relivant.
13060  *
13061  * Fork - LGPL
13062  * <script type="text/javascript">
13063  */
13064  
13065 /**
13066  * @class Roo.PagingToolbar
13067  * @extends Roo.Toolbar
13068  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
13069  * @constructor
13070  * Create a new PagingToolbar
13071  * @param {Object} config The config object
13072  */
13073 Roo.PagingToolbar = function(el, ds, config)
13074 {
13075     // old args format still supported... - xtype is prefered..
13076     if (typeof(el) == 'object' && el.xtype) {
13077         // created from xtype...
13078         config = el;
13079         ds = el.dataSource;
13080         el = config.container;
13081     }
13082     var items = [];
13083     if (config.items) {
13084         items = config.items;
13085         config.items = [];
13086     }
13087     
13088     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13089     this.ds = ds;
13090     this.cursor = 0;
13091     this.renderButtons(this.el);
13092     this.bind(ds);
13093     
13094     // supprot items array.
13095    
13096     Roo.each(items, function(e) {
13097         this.add(Roo.factory(e));
13098     },this);
13099     
13100 };
13101
13102 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13103     /**
13104      * @cfg {Roo.data.Store} dataSource
13105      * The underlying data store providing the paged data
13106      */
13107     /**
13108      * @cfg {String/HTMLElement/Element} container
13109      * container The id or element that will contain the toolbar
13110      */
13111     /**
13112      * @cfg {Boolean} displayInfo
13113      * True to display the displayMsg (defaults to false)
13114      */
13115     /**
13116      * @cfg {Number} pageSize
13117      * The number of records to display per page (defaults to 20)
13118      */
13119     pageSize: 20,
13120     /**
13121      * @cfg {String} displayMsg
13122      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13123      */
13124     displayMsg : 'Displaying {0} - {1} of {2}',
13125     /**
13126      * @cfg {String} emptyMsg
13127      * The message to display when no records are found (defaults to "No data to display")
13128      */
13129     emptyMsg : 'No data to display',
13130     /**
13131      * Customizable piece of the default paging text (defaults to "Page")
13132      * @type String
13133      */
13134     beforePageText : "Page",
13135     /**
13136      * Customizable piece of the default paging text (defaults to "of %0")
13137      * @type String
13138      */
13139     afterPageText : "of {0}",
13140     /**
13141      * Customizable piece of the default paging text (defaults to "First Page")
13142      * @type String
13143      */
13144     firstText : "First Page",
13145     /**
13146      * Customizable piece of the default paging text (defaults to "Previous Page")
13147      * @type String
13148      */
13149     prevText : "Previous Page",
13150     /**
13151      * Customizable piece of the default paging text (defaults to "Next Page")
13152      * @type String
13153      */
13154     nextText : "Next Page",
13155     /**
13156      * Customizable piece of the default paging text (defaults to "Last Page")
13157      * @type String
13158      */
13159     lastText : "Last Page",
13160     /**
13161      * Customizable piece of the default paging text (defaults to "Refresh")
13162      * @type String
13163      */
13164     refreshText : "Refresh",
13165
13166     // private
13167     renderButtons : function(el){
13168         Roo.PagingToolbar.superclass.render.call(this, el);
13169         this.first = this.addButton({
13170             tooltip: this.firstText,
13171             cls: "x-btn-icon x-grid-page-first",
13172             disabled: true,
13173             handler: this.onClick.createDelegate(this, ["first"])
13174         });
13175         this.prev = this.addButton({
13176             tooltip: this.prevText,
13177             cls: "x-btn-icon x-grid-page-prev",
13178             disabled: true,
13179             handler: this.onClick.createDelegate(this, ["prev"])
13180         });
13181         //this.addSeparator();
13182         this.add(this.beforePageText);
13183         this.field = Roo.get(this.addDom({
13184            tag: "input",
13185            type: "text",
13186            size: "3",
13187            value: "1",
13188            cls: "x-grid-page-number"
13189         }).el);
13190         this.field.on("keydown", this.onPagingKeydown, this);
13191         this.field.on("focus", function(){this.dom.select();});
13192         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13193         this.field.setHeight(18);
13194         //this.addSeparator();
13195         this.next = this.addButton({
13196             tooltip: this.nextText,
13197             cls: "x-btn-icon x-grid-page-next",
13198             disabled: true,
13199             handler: this.onClick.createDelegate(this, ["next"])
13200         });
13201         this.last = this.addButton({
13202             tooltip: this.lastText,
13203             cls: "x-btn-icon x-grid-page-last",
13204             disabled: true,
13205             handler: this.onClick.createDelegate(this, ["last"])
13206         });
13207         //this.addSeparator();
13208         this.loading = this.addButton({
13209             tooltip: this.refreshText,
13210             cls: "x-btn-icon x-grid-loading",
13211             handler: this.onClick.createDelegate(this, ["refresh"])
13212         });
13213
13214         if(this.displayInfo){
13215             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13216         }
13217     },
13218
13219     // private
13220     updateInfo : function(){
13221         if(this.displayEl){
13222             var count = this.ds.getCount();
13223             var msg = count == 0 ?
13224                 this.emptyMsg :
13225                 String.format(
13226                     this.displayMsg,
13227                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13228                 );
13229             this.displayEl.update(msg);
13230         }
13231     },
13232
13233     // private
13234     onLoad : function(ds, r, o){
13235        this.cursor = o.params ? o.params.start : 0;
13236        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13237
13238        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13239        this.field.dom.value = ap;
13240        this.first.setDisabled(ap == 1);
13241        this.prev.setDisabled(ap == 1);
13242        this.next.setDisabled(ap == ps);
13243        this.last.setDisabled(ap == ps);
13244        this.loading.enable();
13245        this.updateInfo();
13246     },
13247
13248     // private
13249     getPageData : function(){
13250         var total = this.ds.getTotalCount();
13251         return {
13252             total : total,
13253             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13254             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13255         };
13256     },
13257
13258     // private
13259     onLoadError : function(){
13260         this.loading.enable();
13261     },
13262
13263     // private
13264     onPagingKeydown : function(e){
13265         var k = e.getKey();
13266         var d = this.getPageData();
13267         if(k == e.RETURN){
13268             var v = this.field.dom.value, pageNum;
13269             if(!v || isNaN(pageNum = parseInt(v, 10))){
13270                 this.field.dom.value = d.activePage;
13271                 return;
13272             }
13273             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13274             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13275             e.stopEvent();
13276         }
13277         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))
13278         {
13279           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13280           this.field.dom.value = pageNum;
13281           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13282           e.stopEvent();
13283         }
13284         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13285         {
13286           var v = this.field.dom.value, pageNum; 
13287           var increment = (e.shiftKey) ? 10 : 1;
13288           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13289             increment *= -1;
13290           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13291             this.field.dom.value = d.activePage;
13292             return;
13293           }
13294           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13295           {
13296             this.field.dom.value = parseInt(v, 10) + increment;
13297             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13298             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13299           }
13300           e.stopEvent();
13301         }
13302     },
13303
13304     // private
13305     beforeLoad : function(){
13306         if(this.loading){
13307             this.loading.disable();
13308         }
13309     },
13310
13311     // private
13312     onClick : function(which){
13313         var ds = this.ds;
13314         switch(which){
13315             case "first":
13316                 ds.load({params:{start: 0, limit: this.pageSize}});
13317             break;
13318             case "prev":
13319                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13320             break;
13321             case "next":
13322                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13323             break;
13324             case "last":
13325                 var total = ds.getTotalCount();
13326                 var extra = total % this.pageSize;
13327                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13328                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13329             break;
13330             case "refresh":
13331                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13332             break;
13333         }
13334     },
13335
13336     /**
13337      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13338      * @param {Roo.data.Store} store The data store to unbind
13339      */
13340     unbind : function(ds){
13341         ds.un("beforeload", this.beforeLoad, this);
13342         ds.un("load", this.onLoad, this);
13343         ds.un("loadexception", this.onLoadError, this);
13344         ds.un("remove", this.updateInfo, this);
13345         ds.un("add", this.updateInfo, this);
13346         this.ds = undefined;
13347     },
13348
13349     /**
13350      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13351      * @param {Roo.data.Store} store The data store to bind
13352      */
13353     bind : function(ds){
13354         ds.on("beforeload", this.beforeLoad, this);
13355         ds.on("load", this.onLoad, this);
13356         ds.on("loadexception", this.onLoadError, this);
13357         ds.on("remove", this.updateInfo, this);
13358         ds.on("add", this.updateInfo, this);
13359         this.ds = ds;
13360     }
13361 });/*
13362  * Based on:
13363  * Ext JS Library 1.1.1
13364  * Copyright(c) 2006-2007, Ext JS, LLC.
13365  *
13366  * Originally Released Under LGPL - original licence link has changed is not relivant.
13367  *
13368  * Fork - LGPL
13369  * <script type="text/javascript">
13370  */
13371
13372 /**
13373  * @class Roo.Resizable
13374  * @extends Roo.util.Observable
13375  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13376  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13377  * 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
13378  * the element will be wrapped for you automatically.</p>
13379  * <p>Here is the list of valid resize handles:</p>
13380  * <pre>
13381 Value   Description
13382 ------  -------------------
13383  'n'     north
13384  's'     south
13385  'e'     east
13386  'w'     west
13387  'nw'    northwest
13388  'sw'    southwest
13389  'se'    southeast
13390  'ne'    northeast
13391  'hd'    horizontal drag
13392  'all'   all
13393 </pre>
13394  * <p>Here's an example showing the creation of a typical Resizable:</p>
13395  * <pre><code>
13396 var resizer = new Roo.Resizable("element-id", {
13397     handles: 'all',
13398     minWidth: 200,
13399     minHeight: 100,
13400     maxWidth: 500,
13401     maxHeight: 400,
13402     pinned: true
13403 });
13404 resizer.on("resize", myHandler);
13405 </code></pre>
13406  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13407  * resizer.east.setDisplayed(false);</p>
13408  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13409  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13410  * resize operation's new size (defaults to [0, 0])
13411  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13412  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13413  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13414  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13415  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13416  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13417  * @cfg {Number} width The width of the element in pixels (defaults to null)
13418  * @cfg {Number} height The height of the element in pixels (defaults to null)
13419  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13420  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13421  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13422  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13423  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13424  * in favor of the handles config option (defaults to false)
13425  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13426  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13427  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13428  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13429  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13430  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13431  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13432  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13433  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13434  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13435  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13436  * @constructor
13437  * Create a new resizable component
13438  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13439  * @param {Object} config configuration options
13440   */
13441 Roo.Resizable = function(el, config)
13442 {
13443     this.el = Roo.get(el);
13444
13445     if(config && config.wrap){
13446         config.resizeChild = this.el;
13447         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13448         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13449         this.el.setStyle("overflow", "hidden");
13450         this.el.setPositioning(config.resizeChild.getPositioning());
13451         config.resizeChild.clearPositioning();
13452         if(!config.width || !config.height){
13453             var csize = config.resizeChild.getSize();
13454             this.el.setSize(csize.width, csize.height);
13455         }
13456         if(config.pinned && !config.adjustments){
13457             config.adjustments = "auto";
13458         }
13459     }
13460
13461     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13462     this.proxy.unselectable();
13463     this.proxy.enableDisplayMode('block');
13464
13465     Roo.apply(this, config);
13466
13467     if(this.pinned){
13468         this.disableTrackOver = true;
13469         this.el.addClass("x-resizable-pinned");
13470     }
13471     // if the element isn't positioned, make it relative
13472     var position = this.el.getStyle("position");
13473     if(position != "absolute" && position != "fixed"){
13474         this.el.setStyle("position", "relative");
13475     }
13476     if(!this.handles){ // no handles passed, must be legacy style
13477         this.handles = 's,e,se';
13478         if(this.multiDirectional){
13479             this.handles += ',n,w';
13480         }
13481     }
13482     if(this.handles == "all"){
13483         this.handles = "n s e w ne nw se sw";
13484     }
13485     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13486     var ps = Roo.Resizable.positions;
13487     for(var i = 0, len = hs.length; i < len; i++){
13488         if(hs[i] && ps[hs[i]]){
13489             var pos = ps[hs[i]];
13490             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13491         }
13492     }
13493     // legacy
13494     this.corner = this.southeast;
13495     
13496     // updateBox = the box can move..
13497     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13498         this.updateBox = true;
13499     }
13500
13501     this.activeHandle = null;
13502
13503     if(this.resizeChild){
13504         if(typeof this.resizeChild == "boolean"){
13505             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13506         }else{
13507             this.resizeChild = Roo.get(this.resizeChild, true);
13508         }
13509     }
13510     
13511     if(this.adjustments == "auto"){
13512         var rc = this.resizeChild;
13513         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13514         if(rc && (hw || hn)){
13515             rc.position("relative");
13516             rc.setLeft(hw ? hw.el.getWidth() : 0);
13517             rc.setTop(hn ? hn.el.getHeight() : 0);
13518         }
13519         this.adjustments = [
13520             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13521             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13522         ];
13523     }
13524
13525     if(this.draggable){
13526         this.dd = this.dynamic ?
13527             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13528         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13529     }
13530
13531     // public events
13532     this.addEvents({
13533         /**
13534          * @event beforeresize
13535          * Fired before resize is allowed. Set enabled to false to cancel resize.
13536          * @param {Roo.Resizable} this
13537          * @param {Roo.EventObject} e The mousedown event
13538          */
13539         "beforeresize" : true,
13540         /**
13541          * @event resizing
13542          * Fired a resizing.
13543          * @param {Roo.Resizable} this
13544          * @param {Number} x The new x position
13545          * @param {Number} y The new y position
13546          * @param {Number} w The new w width
13547          * @param {Number} h The new h hight
13548          * @param {Roo.EventObject} e The mouseup event
13549          */
13550         "resizing" : true,
13551         /**
13552          * @event resize
13553          * Fired after a resize.
13554          * @param {Roo.Resizable} this
13555          * @param {Number} width The new width
13556          * @param {Number} height The new height
13557          * @param {Roo.EventObject} e The mouseup event
13558          */
13559         "resize" : true
13560     });
13561
13562     if(this.width !== null && this.height !== null){
13563         this.resizeTo(this.width, this.height);
13564     }else{
13565         this.updateChildSize();
13566     }
13567     if(Roo.isIE){
13568         this.el.dom.style.zoom = 1;
13569     }
13570     Roo.Resizable.superclass.constructor.call(this);
13571 };
13572
13573 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13574         resizeChild : false,
13575         adjustments : [0, 0],
13576         minWidth : 5,
13577         minHeight : 5,
13578         maxWidth : 10000,
13579         maxHeight : 10000,
13580         enabled : true,
13581         animate : false,
13582         duration : .35,
13583         dynamic : false,
13584         handles : false,
13585         multiDirectional : false,
13586         disableTrackOver : false,
13587         easing : 'easeOutStrong',
13588         widthIncrement : 0,
13589         heightIncrement : 0,
13590         pinned : false,
13591         width : null,
13592         height : null,
13593         preserveRatio : false,
13594         transparent: false,
13595         minX: 0,
13596         minY: 0,
13597         draggable: false,
13598
13599         /**
13600          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13601          */
13602         constrainTo: undefined,
13603         /**
13604          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13605          */
13606         resizeRegion: undefined,
13607
13608
13609     /**
13610      * Perform a manual resize
13611      * @param {Number} width
13612      * @param {Number} height
13613      */
13614     resizeTo : function(width, height){
13615         this.el.setSize(width, height);
13616         this.updateChildSize();
13617         this.fireEvent("resize", this, width, height, null);
13618     },
13619
13620     // private
13621     startSizing : function(e, handle){
13622         this.fireEvent("beforeresize", this, e);
13623         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13624
13625             if(!this.overlay){
13626                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13627                 this.overlay.unselectable();
13628                 this.overlay.enableDisplayMode("block");
13629                 this.overlay.on("mousemove", this.onMouseMove, this);
13630                 this.overlay.on("mouseup", this.onMouseUp, this);
13631             }
13632             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13633
13634             this.resizing = true;
13635             this.startBox = this.el.getBox();
13636             this.startPoint = e.getXY();
13637             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13638                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13639
13640             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13641             this.overlay.show();
13642
13643             if(this.constrainTo) {
13644                 var ct = Roo.get(this.constrainTo);
13645                 this.resizeRegion = ct.getRegion().adjust(
13646                     ct.getFrameWidth('t'),
13647                     ct.getFrameWidth('l'),
13648                     -ct.getFrameWidth('b'),
13649                     -ct.getFrameWidth('r')
13650                 );
13651             }
13652
13653             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13654             this.proxy.show();
13655             this.proxy.setBox(this.startBox);
13656             if(!this.dynamic){
13657                 this.proxy.setStyle('visibility', 'visible');
13658             }
13659         }
13660     },
13661
13662     // private
13663     onMouseDown : function(handle, e){
13664         if(this.enabled){
13665             e.stopEvent();
13666             this.activeHandle = handle;
13667             this.startSizing(e, handle);
13668         }
13669     },
13670
13671     // private
13672     onMouseUp : function(e){
13673         var size = this.resizeElement();
13674         this.resizing = false;
13675         this.handleOut();
13676         this.overlay.hide();
13677         this.proxy.hide();
13678         this.fireEvent("resize", this, size.width, size.height, e);
13679     },
13680
13681     // private
13682     updateChildSize : function(){
13683         
13684         if(this.resizeChild){
13685             var el = this.el;
13686             var child = this.resizeChild;
13687             var adj = this.adjustments;
13688             if(el.dom.offsetWidth){
13689                 var b = el.getSize(true);
13690                 child.setSize(b.width+adj[0], b.height+adj[1]);
13691             }
13692             // Second call here for IE
13693             // The first call enables instant resizing and
13694             // the second call corrects scroll bars if they
13695             // exist
13696             if(Roo.isIE){
13697                 setTimeout(function(){
13698                     if(el.dom.offsetWidth){
13699                         var b = el.getSize(true);
13700                         child.setSize(b.width+adj[0], b.height+adj[1]);
13701                     }
13702                 }, 10);
13703             }
13704         }
13705     },
13706
13707     // private
13708     snap : function(value, inc, min){
13709         if(!inc || !value) return value;
13710         var newValue = value;
13711         var m = value % inc;
13712         if(m > 0){
13713             if(m > (inc/2)){
13714                 newValue = value + (inc-m);
13715             }else{
13716                 newValue = value - m;
13717             }
13718         }
13719         return Math.max(min, newValue);
13720     },
13721
13722     // private
13723     resizeElement : function(){
13724         var box = this.proxy.getBox();
13725         if(this.updateBox){
13726             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13727         }else{
13728             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13729         }
13730         this.updateChildSize();
13731         if(!this.dynamic){
13732             this.proxy.hide();
13733         }
13734         return box;
13735     },
13736
13737     // private
13738     constrain : function(v, diff, m, mx){
13739         if(v - diff < m){
13740             diff = v - m;
13741         }else if(v - diff > mx){
13742             diff = mx - v;
13743         }
13744         return diff;
13745     },
13746
13747     // private
13748     onMouseMove : function(e){
13749         
13750         if(this.enabled){
13751             try{// try catch so if something goes wrong the user doesn't get hung
13752
13753             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13754                 return;
13755             }
13756
13757             //var curXY = this.startPoint;
13758             var curSize = this.curSize || this.startBox;
13759             var x = this.startBox.x, y = this.startBox.y;
13760             var ox = x, oy = y;
13761             var w = curSize.width, h = curSize.height;
13762             var ow = w, oh = h;
13763             var mw = this.minWidth, mh = this.minHeight;
13764             var mxw = this.maxWidth, mxh = this.maxHeight;
13765             var wi = this.widthIncrement;
13766             var hi = this.heightIncrement;
13767
13768             var eventXY = e.getXY();
13769             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13770             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13771
13772             var pos = this.activeHandle.position;
13773
13774             switch(pos){
13775                 case "east":
13776                     w += diffX;
13777                     w = Math.min(Math.max(mw, w), mxw);
13778                     break;
13779              
13780                 case "south":
13781                     h += diffY;
13782                     h = Math.min(Math.max(mh, h), mxh);
13783                     break;
13784                 case "southeast":
13785                     w += diffX;
13786                     h += diffY;
13787                     w = Math.min(Math.max(mw, w), mxw);
13788                     h = Math.min(Math.max(mh, h), mxh);
13789                     break;
13790                 case "north":
13791                     diffY = this.constrain(h, diffY, mh, mxh);
13792                     y += diffY;
13793                     h -= diffY;
13794                     break;
13795                 case "hdrag":
13796                     
13797                     if (wi) {
13798                         var adiffX = Math.abs(diffX);
13799                         var sub = (adiffX % wi); // how much 
13800                         if (sub > (wi/2)) { // far enough to snap
13801                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13802                         } else {
13803                             // remove difference.. 
13804                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13805                         }
13806                     }
13807                     x += diffX;
13808                     x = Math.max(this.minX, x);
13809                     break;
13810                 case "west":
13811                     diffX = this.constrain(w, diffX, mw, mxw);
13812                     x += diffX;
13813                     w -= diffX;
13814                     break;
13815                 case "northeast":
13816                     w += diffX;
13817                     w = Math.min(Math.max(mw, w), mxw);
13818                     diffY = this.constrain(h, diffY, mh, mxh);
13819                     y += diffY;
13820                     h -= diffY;
13821                     break;
13822                 case "northwest":
13823                     diffX = this.constrain(w, diffX, mw, mxw);
13824                     diffY = this.constrain(h, diffY, mh, mxh);
13825                     y += diffY;
13826                     h -= diffY;
13827                     x += diffX;
13828                     w -= diffX;
13829                     break;
13830                case "southwest":
13831                     diffX = this.constrain(w, diffX, mw, mxw);
13832                     h += diffY;
13833                     h = Math.min(Math.max(mh, h), mxh);
13834                     x += diffX;
13835                     w -= diffX;
13836                     break;
13837             }
13838
13839             var sw = this.snap(w, wi, mw);
13840             var sh = this.snap(h, hi, mh);
13841             if(sw != w || sh != h){
13842                 switch(pos){
13843                     case "northeast":
13844                         y -= sh - h;
13845                     break;
13846                     case "north":
13847                         y -= sh - h;
13848                         break;
13849                     case "southwest":
13850                         x -= sw - w;
13851                     break;
13852                     case "west":
13853                         x -= sw - w;
13854                         break;
13855                     case "northwest":
13856                         x -= sw - w;
13857                         y -= sh - h;
13858                     break;
13859                 }
13860                 w = sw;
13861                 h = sh;
13862             }
13863
13864             if(this.preserveRatio){
13865                 switch(pos){
13866                     case "southeast":
13867                     case "east":
13868                         h = oh * (w/ow);
13869                         h = Math.min(Math.max(mh, h), mxh);
13870                         w = ow * (h/oh);
13871                        break;
13872                     case "south":
13873                         w = ow * (h/oh);
13874                         w = Math.min(Math.max(mw, w), mxw);
13875                         h = oh * (w/ow);
13876                         break;
13877                     case "northeast":
13878                         w = ow * (h/oh);
13879                         w = Math.min(Math.max(mw, w), mxw);
13880                         h = oh * (w/ow);
13881                     break;
13882                     case "north":
13883                         var tw = w;
13884                         w = ow * (h/oh);
13885                         w = Math.min(Math.max(mw, w), mxw);
13886                         h = oh * (w/ow);
13887                         x += (tw - w) / 2;
13888                         break;
13889                     case "southwest":
13890                         h = oh * (w/ow);
13891                         h = Math.min(Math.max(mh, h), mxh);
13892                         var tw = w;
13893                         w = ow * (h/oh);
13894                         x += tw - w;
13895                         break;
13896                     case "west":
13897                         var th = h;
13898                         h = oh * (w/ow);
13899                         h = Math.min(Math.max(mh, h), mxh);
13900                         y += (th - h) / 2;
13901                         var tw = w;
13902                         w = ow * (h/oh);
13903                         x += tw - w;
13904                        break;
13905                     case "northwest":
13906                         var tw = w;
13907                         var th = h;
13908                         h = oh * (w/ow);
13909                         h = Math.min(Math.max(mh, h), mxh);
13910                         w = ow * (h/oh);
13911                         y += th - h;
13912                         x += tw - w;
13913                        break;
13914
13915                 }
13916             }
13917             if (pos == 'hdrag') {
13918                 w = ow;
13919             }
13920             this.proxy.setBounds(x, y, w, h);
13921             if(this.dynamic){
13922                 this.resizeElement();
13923             }
13924             }catch(e){}
13925         }
13926         this.fireEvent("resizing", this, x, y, w, h, e);
13927     },
13928
13929     // private
13930     handleOver : function(){
13931         if(this.enabled){
13932             this.el.addClass("x-resizable-over");
13933         }
13934     },
13935
13936     // private
13937     handleOut : function(){
13938         if(!this.resizing){
13939             this.el.removeClass("x-resizable-over");
13940         }
13941     },
13942
13943     /**
13944      * Returns the element this component is bound to.
13945      * @return {Roo.Element}
13946      */
13947     getEl : function(){
13948         return this.el;
13949     },
13950
13951     /**
13952      * Returns the resizeChild element (or null).
13953      * @return {Roo.Element}
13954      */
13955     getResizeChild : function(){
13956         return this.resizeChild;
13957     },
13958     groupHandler : function()
13959     {
13960         
13961     },
13962     /**
13963      * Destroys this resizable. If the element was wrapped and
13964      * removeEl is not true then the element remains.
13965      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13966      */
13967     destroy : function(removeEl){
13968         this.proxy.remove();
13969         if(this.overlay){
13970             this.overlay.removeAllListeners();
13971             this.overlay.remove();
13972         }
13973         var ps = Roo.Resizable.positions;
13974         for(var k in ps){
13975             if(typeof ps[k] != "function" && this[ps[k]]){
13976                 var h = this[ps[k]];
13977                 h.el.removeAllListeners();
13978                 h.el.remove();
13979             }
13980         }
13981         if(removeEl){
13982             this.el.update("");
13983             this.el.remove();
13984         }
13985     }
13986 });
13987
13988 // private
13989 // hash to map config positions to true positions
13990 Roo.Resizable.positions = {
13991     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13992     hd: "hdrag"
13993 };
13994
13995 // private
13996 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13997     if(!this.tpl){
13998         // only initialize the template if resizable is used
13999         var tpl = Roo.DomHelper.createTemplate(
14000             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
14001         );
14002         tpl.compile();
14003         Roo.Resizable.Handle.prototype.tpl = tpl;
14004     }
14005     this.position = pos;
14006     this.rz = rz;
14007     // show north drag fro topdra
14008     var handlepos = pos == 'hdrag' ? 'north' : pos;
14009     
14010     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
14011     if (pos == 'hdrag') {
14012         this.el.setStyle('cursor', 'pointer');
14013     }
14014     this.el.unselectable();
14015     if(transparent){
14016         this.el.setOpacity(0);
14017     }
14018     this.el.on("mousedown", this.onMouseDown, this);
14019     if(!disableTrackOver){
14020         this.el.on("mouseover", this.onMouseOver, this);
14021         this.el.on("mouseout", this.onMouseOut, this);
14022     }
14023 };
14024
14025 // private
14026 Roo.Resizable.Handle.prototype = {
14027     afterResize : function(rz){
14028         // do nothing
14029     },
14030     // private
14031     onMouseDown : function(e){
14032         this.rz.onMouseDown(this, e);
14033     },
14034     // private
14035     onMouseOver : function(e){
14036         this.rz.handleOver(this, e);
14037     },
14038     // private
14039     onMouseOut : function(e){
14040         this.rz.handleOut(this, e);
14041     }
14042 };/*
14043  * Based on:
14044  * Ext JS Library 1.1.1
14045  * Copyright(c) 2006-2007, Ext JS, LLC.
14046  *
14047  * Originally Released Under LGPL - original licence link has changed is not relivant.
14048  *
14049  * Fork - LGPL
14050  * <script type="text/javascript">
14051  */
14052
14053 /**
14054  * @class Roo.Editor
14055  * @extends Roo.Component
14056  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
14057  * @constructor
14058  * Create a new Editor
14059  * @param {Roo.form.Field} field The Field object (or descendant)
14060  * @param {Object} config The config object
14061  */
14062 Roo.Editor = function(field, config){
14063     Roo.Editor.superclass.constructor.call(this, config);
14064     this.field = field;
14065     this.addEvents({
14066         /**
14067              * @event beforestartedit
14068              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14069              * false from the handler of this event.
14070              * @param {Editor} this
14071              * @param {Roo.Element} boundEl The underlying element bound to this editor
14072              * @param {Mixed} value The field value being set
14073              */
14074         "beforestartedit" : true,
14075         /**
14076              * @event startedit
14077              * Fires when this editor is displayed
14078              * @param {Roo.Element} boundEl The underlying element bound to this editor
14079              * @param {Mixed} value The starting field value
14080              */
14081         "startedit" : true,
14082         /**
14083              * @event beforecomplete
14084              * Fires after a change has been made to the field, but before the change is reflected in the underlying
14085              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
14086              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
14087              * event will not fire since no edit actually occurred.
14088              * @param {Editor} this
14089              * @param {Mixed} value The current field value
14090              * @param {Mixed} startValue The original field value
14091              */
14092         "beforecomplete" : true,
14093         /**
14094              * @event complete
14095              * Fires after editing is complete and any changed value has been written to the underlying field.
14096              * @param {Editor} this
14097              * @param {Mixed} value The current field value
14098              * @param {Mixed} startValue The original field value
14099              */
14100         "complete" : true,
14101         /**
14102          * @event specialkey
14103          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
14104          * {@link Roo.EventObject#getKey} to determine which key was pressed.
14105          * @param {Roo.form.Field} this
14106          * @param {Roo.EventObject} e The event object
14107          */
14108         "specialkey" : true
14109     });
14110 };
14111
14112 Roo.extend(Roo.Editor, Roo.Component, {
14113     /**
14114      * @cfg {Boolean/String} autosize
14115      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14116      * or "height" to adopt the height only (defaults to false)
14117      */
14118     /**
14119      * @cfg {Boolean} revertInvalid
14120      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14121      * validation fails (defaults to true)
14122      */
14123     /**
14124      * @cfg {Boolean} ignoreNoChange
14125      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14126      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14127      * will never be ignored.
14128      */
14129     /**
14130      * @cfg {Boolean} hideEl
14131      * False to keep the bound element visible while the editor is displayed (defaults to true)
14132      */
14133     /**
14134      * @cfg {Mixed} value
14135      * The data value of the underlying field (defaults to "")
14136      */
14137     value : "",
14138     /**
14139      * @cfg {String} alignment
14140      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14141      */
14142     alignment: "c-c?",
14143     /**
14144      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14145      * for bottom-right shadow (defaults to "frame")
14146      */
14147     shadow : "frame",
14148     /**
14149      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14150      */
14151     constrain : false,
14152     /**
14153      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14154      */
14155     completeOnEnter : false,
14156     /**
14157      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14158      */
14159     cancelOnEsc : false,
14160     /**
14161      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14162      */
14163     updateEl : false,
14164
14165     // private
14166     onRender : function(ct, position){
14167         this.el = new Roo.Layer({
14168             shadow: this.shadow,
14169             cls: "x-editor",
14170             parentEl : ct,
14171             shim : this.shim,
14172             shadowOffset:4,
14173             id: this.id,
14174             constrain: this.constrain
14175         });
14176         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14177         if(this.field.msgTarget != 'title'){
14178             this.field.msgTarget = 'qtip';
14179         }
14180         this.field.render(this.el);
14181         if(Roo.isGecko){
14182             this.field.el.dom.setAttribute('autocomplete', 'off');
14183         }
14184         this.field.on("specialkey", this.onSpecialKey, this);
14185         if(this.swallowKeys){
14186             this.field.el.swallowEvent(['keydown','keypress']);
14187         }
14188         this.field.show();
14189         this.field.on("blur", this.onBlur, this);
14190         if(this.field.grow){
14191             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14192         }
14193     },
14194
14195     onSpecialKey : function(field, e)
14196     {
14197         //Roo.log('editor onSpecialKey');
14198         if(this.completeOnEnter && e.getKey() == e.ENTER){
14199             e.stopEvent();
14200             this.completeEdit();
14201             return;
14202         }
14203         // do not fire special key otherwise it might hide close the editor...
14204         if(e.getKey() == e.ENTER){    
14205             return;
14206         }
14207         if(this.cancelOnEsc && e.getKey() == e.ESC){
14208             this.cancelEdit();
14209             return;
14210         } 
14211         this.fireEvent('specialkey', field, e);
14212     
14213     },
14214
14215     /**
14216      * Starts the editing process and shows the editor.
14217      * @param {String/HTMLElement/Element} el The element to edit
14218      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14219       * to the innerHTML of el.
14220      */
14221     startEdit : function(el, value){
14222         if(this.editing){
14223             this.completeEdit();
14224         }
14225         this.boundEl = Roo.get(el);
14226         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14227         if(!this.rendered){
14228             this.render(this.parentEl || document.body);
14229         }
14230         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14231             return;
14232         }
14233         this.startValue = v;
14234         this.field.setValue(v);
14235         if(this.autoSize){
14236             var sz = this.boundEl.getSize();
14237             switch(this.autoSize){
14238                 case "width":
14239                 this.setSize(sz.width,  "");
14240                 break;
14241                 case "height":
14242                 this.setSize("",  sz.height);
14243                 break;
14244                 default:
14245                 this.setSize(sz.width,  sz.height);
14246             }
14247         }
14248         this.el.alignTo(this.boundEl, this.alignment);
14249         this.editing = true;
14250         if(Roo.QuickTips){
14251             Roo.QuickTips.disable();
14252         }
14253         this.show();
14254     },
14255
14256     /**
14257      * Sets the height and width of this editor.
14258      * @param {Number} width The new width
14259      * @param {Number} height The new height
14260      */
14261     setSize : function(w, h){
14262         this.field.setSize(w, h);
14263         if(this.el){
14264             this.el.sync();
14265         }
14266     },
14267
14268     /**
14269      * Realigns the editor to the bound field based on the current alignment config value.
14270      */
14271     realign : function(){
14272         this.el.alignTo(this.boundEl, this.alignment);
14273     },
14274
14275     /**
14276      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14277      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14278      */
14279     completeEdit : function(remainVisible){
14280         if(!this.editing){
14281             return;
14282         }
14283         var v = this.getValue();
14284         if(this.revertInvalid !== false && !this.field.isValid()){
14285             v = this.startValue;
14286             this.cancelEdit(true);
14287         }
14288         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14289             this.editing = false;
14290             this.hide();
14291             return;
14292         }
14293         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14294             this.editing = false;
14295             if(this.updateEl && this.boundEl){
14296                 this.boundEl.update(v);
14297             }
14298             if(remainVisible !== true){
14299                 this.hide();
14300             }
14301             this.fireEvent("complete", this, v, this.startValue);
14302         }
14303     },
14304
14305     // private
14306     onShow : function(){
14307         this.el.show();
14308         if(this.hideEl !== false){
14309             this.boundEl.hide();
14310         }
14311         this.field.show();
14312         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14313             this.fixIEFocus = true;
14314             this.deferredFocus.defer(50, this);
14315         }else{
14316             this.field.focus();
14317         }
14318         this.fireEvent("startedit", this.boundEl, this.startValue);
14319     },
14320
14321     deferredFocus : function(){
14322         if(this.editing){
14323             this.field.focus();
14324         }
14325     },
14326
14327     /**
14328      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14329      * reverted to the original starting value.
14330      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14331      * cancel (defaults to false)
14332      */
14333     cancelEdit : function(remainVisible){
14334         if(this.editing){
14335             this.setValue(this.startValue);
14336             if(remainVisible !== true){
14337                 this.hide();
14338             }
14339         }
14340     },
14341
14342     // private
14343     onBlur : function(){
14344         if(this.allowBlur !== true && this.editing){
14345             this.completeEdit();
14346         }
14347     },
14348
14349     // private
14350     onHide : function(){
14351         if(this.editing){
14352             this.completeEdit();
14353             return;
14354         }
14355         this.field.blur();
14356         if(this.field.collapse){
14357             this.field.collapse();
14358         }
14359         this.el.hide();
14360         if(this.hideEl !== false){
14361             this.boundEl.show();
14362         }
14363         if(Roo.QuickTips){
14364             Roo.QuickTips.enable();
14365         }
14366     },
14367
14368     /**
14369      * Sets the data value of the editor
14370      * @param {Mixed} value Any valid value supported by the underlying field
14371      */
14372     setValue : function(v){
14373         this.field.setValue(v);
14374     },
14375
14376     /**
14377      * Gets the data value of the editor
14378      * @return {Mixed} The data value
14379      */
14380     getValue : function(){
14381         return this.field.getValue();
14382     }
14383 });/*
14384  * Based on:
14385  * Ext JS Library 1.1.1
14386  * Copyright(c) 2006-2007, Ext JS, LLC.
14387  *
14388  * Originally Released Under LGPL - original licence link has changed is not relivant.
14389  *
14390  * Fork - LGPL
14391  * <script type="text/javascript">
14392  */
14393  
14394 /**
14395  * @class Roo.BasicDialog
14396  * @extends Roo.util.Observable
14397  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14398  * <pre><code>
14399 var dlg = new Roo.BasicDialog("my-dlg", {
14400     height: 200,
14401     width: 300,
14402     minHeight: 100,
14403     minWidth: 150,
14404     modal: true,
14405     proxyDrag: true,
14406     shadow: true
14407 });
14408 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14409 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14410 dlg.addButton('Cancel', dlg.hide, dlg);
14411 dlg.show();
14412 </code></pre>
14413   <b>A Dialog should always be a direct child of the body element.</b>
14414  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14415  * @cfg {String} title Default text to display in the title bar (defaults to null)
14416  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14417  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14418  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14419  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14420  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14421  * (defaults to null with no animation)
14422  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14423  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14424  * property for valid values (defaults to 'all')
14425  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14426  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14427  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14428  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14429  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14430  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14431  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14432  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14433  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14434  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14435  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14436  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14437  * draggable = true (defaults to false)
14438  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14439  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14440  * shadow (defaults to false)
14441  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14442  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14443  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14444  * @cfg {Array} buttons Array of buttons
14445  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14446  * @constructor
14447  * Create a new BasicDialog.
14448  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14449  * @param {Object} config Configuration options
14450  */
14451 Roo.BasicDialog = function(el, config){
14452     this.el = Roo.get(el);
14453     var dh = Roo.DomHelper;
14454     if(!this.el && config && config.autoCreate){
14455         if(typeof config.autoCreate == "object"){
14456             if(!config.autoCreate.id){
14457                 config.autoCreate.id = el;
14458             }
14459             this.el = dh.append(document.body,
14460                         config.autoCreate, true);
14461         }else{
14462             this.el = dh.append(document.body,
14463                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14464         }
14465     }
14466     el = this.el;
14467     el.setDisplayed(true);
14468     el.hide = this.hideAction;
14469     this.id = el.id;
14470     el.addClass("x-dlg");
14471
14472     Roo.apply(this, config);
14473
14474     this.proxy = el.createProxy("x-dlg-proxy");
14475     this.proxy.hide = this.hideAction;
14476     this.proxy.setOpacity(.5);
14477     this.proxy.hide();
14478
14479     if(config.width){
14480         el.setWidth(config.width);
14481     }
14482     if(config.height){
14483         el.setHeight(config.height);
14484     }
14485     this.size = el.getSize();
14486     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14487         this.xy = [config.x,config.y];
14488     }else{
14489         this.xy = el.getCenterXY(true);
14490     }
14491     /** The header element @type Roo.Element */
14492     this.header = el.child("> .x-dlg-hd");
14493     /** The body element @type Roo.Element */
14494     this.body = el.child("> .x-dlg-bd");
14495     /** The footer element @type Roo.Element */
14496     this.footer = el.child("> .x-dlg-ft");
14497
14498     if(!this.header){
14499         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14500     }
14501     if(!this.body){
14502         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14503     }
14504
14505     this.header.unselectable();
14506     if(this.title){
14507         this.header.update(this.title);
14508     }
14509     // this element allows the dialog to be focused for keyboard event
14510     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14511     this.focusEl.swallowEvent("click", true);
14512
14513     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14514
14515     // wrap the body and footer for special rendering
14516     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14517     if(this.footer){
14518         this.bwrap.dom.appendChild(this.footer.dom);
14519     }
14520
14521     this.bg = this.el.createChild({
14522         tag: "div", cls:"x-dlg-bg",
14523         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14524     });
14525     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14526
14527
14528     if(this.autoScroll !== false && !this.autoTabs){
14529         this.body.setStyle("overflow", "auto");
14530     }
14531
14532     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14533
14534     if(this.closable !== false){
14535         this.el.addClass("x-dlg-closable");
14536         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14537         this.close.on("click", this.closeClick, this);
14538         this.close.addClassOnOver("x-dlg-close-over");
14539     }
14540     if(this.collapsible !== false){
14541         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14542         this.collapseBtn.on("click", this.collapseClick, this);
14543         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14544         this.header.on("dblclick", this.collapseClick, this);
14545     }
14546     if(this.resizable !== false){
14547         this.el.addClass("x-dlg-resizable");
14548         this.resizer = new Roo.Resizable(el, {
14549             minWidth: this.minWidth || 80,
14550             minHeight:this.minHeight || 80,
14551             handles: this.resizeHandles || "all",
14552             pinned: true
14553         });
14554         this.resizer.on("beforeresize", this.beforeResize, this);
14555         this.resizer.on("resize", this.onResize, this);
14556     }
14557     if(this.draggable !== false){
14558         el.addClass("x-dlg-draggable");
14559         if (!this.proxyDrag) {
14560             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14561         }
14562         else {
14563             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14564         }
14565         dd.setHandleElId(this.header.id);
14566         dd.endDrag = this.endMove.createDelegate(this);
14567         dd.startDrag = this.startMove.createDelegate(this);
14568         dd.onDrag = this.onDrag.createDelegate(this);
14569         dd.scroll = false;
14570         this.dd = dd;
14571     }
14572     if(this.modal){
14573         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14574         this.mask.enableDisplayMode("block");
14575         this.mask.hide();
14576         this.el.addClass("x-dlg-modal");
14577     }
14578     if(this.shadow){
14579         this.shadow = new Roo.Shadow({
14580             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14581             offset : this.shadowOffset
14582         });
14583     }else{
14584         this.shadowOffset = 0;
14585     }
14586     if(Roo.useShims && this.shim !== false){
14587         this.shim = this.el.createShim();
14588         this.shim.hide = this.hideAction;
14589         this.shim.hide();
14590     }else{
14591         this.shim = false;
14592     }
14593     if(this.autoTabs){
14594         this.initTabs();
14595     }
14596     if (this.buttons) { 
14597         var bts= this.buttons;
14598         this.buttons = [];
14599         Roo.each(bts, function(b) {
14600             this.addButton(b);
14601         }, this);
14602     }
14603     
14604     
14605     this.addEvents({
14606         /**
14607          * @event keydown
14608          * Fires when a key is pressed
14609          * @param {Roo.BasicDialog} this
14610          * @param {Roo.EventObject} e
14611          */
14612         "keydown" : true,
14613         /**
14614          * @event move
14615          * Fires when this dialog is moved by the user.
14616          * @param {Roo.BasicDialog} this
14617          * @param {Number} x The new page X
14618          * @param {Number} y The new page Y
14619          */
14620         "move" : true,
14621         /**
14622          * @event resize
14623          * Fires when this dialog is resized by the user.
14624          * @param {Roo.BasicDialog} this
14625          * @param {Number} width The new width
14626          * @param {Number} height The new height
14627          */
14628         "resize" : true,
14629         /**
14630          * @event beforehide
14631          * Fires before this dialog is hidden.
14632          * @param {Roo.BasicDialog} this
14633          */
14634         "beforehide" : true,
14635         /**
14636          * @event hide
14637          * Fires when this dialog is hidden.
14638          * @param {Roo.BasicDialog} this
14639          */
14640         "hide" : true,
14641         /**
14642          * @event beforeshow
14643          * Fires before this dialog is shown.
14644          * @param {Roo.BasicDialog} this
14645          */
14646         "beforeshow" : true,
14647         /**
14648          * @event show
14649          * Fires when this dialog is shown.
14650          * @param {Roo.BasicDialog} this
14651          */
14652         "show" : true
14653     });
14654     el.on("keydown", this.onKeyDown, this);
14655     el.on("mousedown", this.toFront, this);
14656     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14657     this.el.hide();
14658     Roo.DialogManager.register(this);
14659     Roo.BasicDialog.superclass.constructor.call(this);
14660 };
14661
14662 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14663     shadowOffset: Roo.isIE ? 6 : 5,
14664     minHeight: 80,
14665     minWidth: 200,
14666     minButtonWidth: 75,
14667     defaultButton: null,
14668     buttonAlign: "right",
14669     tabTag: 'div',
14670     firstShow: true,
14671
14672     /**
14673      * Sets the dialog title text
14674      * @param {String} text The title text to display
14675      * @return {Roo.BasicDialog} this
14676      */
14677     setTitle : function(text){
14678         this.header.update(text);
14679         return this;
14680     },
14681
14682     // private
14683     closeClick : function(){
14684         this.hide();
14685     },
14686
14687     // private
14688     collapseClick : function(){
14689         this[this.collapsed ? "expand" : "collapse"]();
14690     },
14691
14692     /**
14693      * Collapses the dialog to its minimized state (only the title bar is visible).
14694      * Equivalent to the user clicking the collapse dialog button.
14695      */
14696     collapse : function(){
14697         if(!this.collapsed){
14698             this.collapsed = true;
14699             this.el.addClass("x-dlg-collapsed");
14700             this.restoreHeight = this.el.getHeight();
14701             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14702         }
14703     },
14704
14705     /**
14706      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14707      * clicking the expand dialog button.
14708      */
14709     expand : function(){
14710         if(this.collapsed){
14711             this.collapsed = false;
14712             this.el.removeClass("x-dlg-collapsed");
14713             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14714         }
14715     },
14716
14717     /**
14718      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14719      * @return {Roo.TabPanel} The tabs component
14720      */
14721     initTabs : function(){
14722         var tabs = this.getTabs();
14723         while(tabs.getTab(0)){
14724             tabs.removeTab(0);
14725         }
14726         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14727             var dom = el.dom;
14728             tabs.addTab(Roo.id(dom), dom.title);
14729             dom.title = "";
14730         });
14731         tabs.activate(0);
14732         return tabs;
14733     },
14734
14735     // private
14736     beforeResize : function(){
14737         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14738     },
14739
14740     // private
14741     onResize : function(){
14742         this.refreshSize();
14743         this.syncBodyHeight();
14744         this.adjustAssets();
14745         this.focus();
14746         this.fireEvent("resize", this, this.size.width, this.size.height);
14747     },
14748
14749     // private
14750     onKeyDown : function(e){
14751         if(this.isVisible()){
14752             this.fireEvent("keydown", this, e);
14753         }
14754     },
14755
14756     /**
14757      * Resizes the dialog.
14758      * @param {Number} width
14759      * @param {Number} height
14760      * @return {Roo.BasicDialog} this
14761      */
14762     resizeTo : function(width, height){
14763         this.el.setSize(width, height);
14764         this.size = {width: width, height: height};
14765         this.syncBodyHeight();
14766         if(this.fixedcenter){
14767             this.center();
14768         }
14769         if(this.isVisible()){
14770             this.constrainXY();
14771             this.adjustAssets();
14772         }
14773         this.fireEvent("resize", this, width, height);
14774         return this;
14775     },
14776
14777
14778     /**
14779      * Resizes the dialog to fit the specified content size.
14780      * @param {Number} width
14781      * @param {Number} height
14782      * @return {Roo.BasicDialog} this
14783      */
14784     setContentSize : function(w, h){
14785         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14786         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14787         //if(!this.el.isBorderBox()){
14788             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14789             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14790         //}
14791         if(this.tabs){
14792             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14793             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14794         }
14795         this.resizeTo(w, h);
14796         return this;
14797     },
14798
14799     /**
14800      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14801      * executed in response to a particular key being pressed while the dialog is active.
14802      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14803      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14804      * @param {Function} fn The function to call
14805      * @param {Object} scope (optional) The scope of the function
14806      * @return {Roo.BasicDialog} this
14807      */
14808     addKeyListener : function(key, fn, scope){
14809         var keyCode, shift, ctrl, alt;
14810         if(typeof key == "object" && !(key instanceof Array)){
14811             keyCode = key["key"];
14812             shift = key["shift"];
14813             ctrl = key["ctrl"];
14814             alt = key["alt"];
14815         }else{
14816             keyCode = key;
14817         }
14818         var handler = function(dlg, e){
14819             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14820                 var k = e.getKey();
14821                 if(keyCode instanceof Array){
14822                     for(var i = 0, len = keyCode.length; i < len; i++){
14823                         if(keyCode[i] == k){
14824                           fn.call(scope || window, dlg, k, e);
14825                           return;
14826                         }
14827                     }
14828                 }else{
14829                     if(k == keyCode){
14830                         fn.call(scope || window, dlg, k, e);
14831                     }
14832                 }
14833             }
14834         };
14835         this.on("keydown", handler);
14836         return this;
14837     },
14838
14839     /**
14840      * Returns the TabPanel component (creates it if it doesn't exist).
14841      * Note: If you wish to simply check for the existence of tabs without creating them,
14842      * check for a null 'tabs' property.
14843      * @return {Roo.TabPanel} The tabs component
14844      */
14845     getTabs : function(){
14846         if(!this.tabs){
14847             this.el.addClass("x-dlg-auto-tabs");
14848             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14849             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14850         }
14851         return this.tabs;
14852     },
14853
14854     /**
14855      * Adds a button to the footer section of the dialog.
14856      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14857      * object or a valid Roo.DomHelper element config
14858      * @param {Function} handler The function called when the button is clicked
14859      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14860      * @return {Roo.Button} The new button
14861      */
14862     addButton : function(config, handler, scope){
14863         var dh = Roo.DomHelper;
14864         if(!this.footer){
14865             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14866         }
14867         if(!this.btnContainer){
14868             var tb = this.footer.createChild({
14869
14870                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14871                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14872             }, null, true);
14873             this.btnContainer = tb.firstChild.firstChild.firstChild;
14874         }
14875         var bconfig = {
14876             handler: handler,
14877             scope: scope,
14878             minWidth: this.minButtonWidth,
14879             hideParent:true
14880         };
14881         if(typeof config == "string"){
14882             bconfig.text = config;
14883         }else{
14884             if(config.tag){
14885                 bconfig.dhconfig = config;
14886             }else{
14887                 Roo.apply(bconfig, config);
14888             }
14889         }
14890         var fc = false;
14891         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14892             bconfig.position = Math.max(0, bconfig.position);
14893             fc = this.btnContainer.childNodes[bconfig.position];
14894         }
14895          
14896         var btn = new Roo.Button(
14897             fc ? 
14898                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14899                 : this.btnContainer.appendChild(document.createElement("td")),
14900             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14901             bconfig
14902         );
14903         this.syncBodyHeight();
14904         if(!this.buttons){
14905             /**
14906              * Array of all the buttons that have been added to this dialog via addButton
14907              * @type Array
14908              */
14909             this.buttons = [];
14910         }
14911         this.buttons.push(btn);
14912         return btn;
14913     },
14914
14915     /**
14916      * Sets the default button to be focused when the dialog is displayed.
14917      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14918      * @return {Roo.BasicDialog} this
14919      */
14920     setDefaultButton : function(btn){
14921         this.defaultButton = btn;
14922         return this;
14923     },
14924
14925     // private
14926     getHeaderFooterHeight : function(safe){
14927         var height = 0;
14928         if(this.header){
14929            height += this.header.getHeight();
14930         }
14931         if(this.footer){
14932            var fm = this.footer.getMargins();
14933             height += (this.footer.getHeight()+fm.top+fm.bottom);
14934         }
14935         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14936         height += this.centerBg.getPadding("tb");
14937         return height;
14938     },
14939
14940     // private
14941     syncBodyHeight : function()
14942     {
14943         var bd = this.body, // the text
14944             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14945             bw = this.bwrap;
14946         var height = this.size.height - this.getHeaderFooterHeight(false);
14947         bd.setHeight(height-bd.getMargins("tb"));
14948         var hh = this.header.getHeight();
14949         var h = this.size.height-hh;
14950         cb.setHeight(h);
14951         
14952         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14953         bw.setHeight(h-cb.getPadding("tb"));
14954         
14955         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14956         bd.setWidth(bw.getWidth(true));
14957         if(this.tabs){
14958             this.tabs.syncHeight();
14959             if(Roo.isIE){
14960                 this.tabs.el.repaint();
14961             }
14962         }
14963     },
14964
14965     /**
14966      * Restores the previous state of the dialog if Roo.state is configured.
14967      * @return {Roo.BasicDialog} this
14968      */
14969     restoreState : function(){
14970         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14971         if(box && box.width){
14972             this.xy = [box.x, box.y];
14973             this.resizeTo(box.width, box.height);
14974         }
14975         return this;
14976     },
14977
14978     // private
14979     beforeShow : function(){
14980         this.expand();
14981         if(this.fixedcenter){
14982             this.xy = this.el.getCenterXY(true);
14983         }
14984         if(this.modal){
14985             Roo.get(document.body).addClass("x-body-masked");
14986             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14987             this.mask.show();
14988         }
14989         this.constrainXY();
14990     },
14991
14992     // private
14993     animShow : function(){
14994         var b = Roo.get(this.animateTarget).getBox();
14995         this.proxy.setSize(b.width, b.height);
14996         this.proxy.setLocation(b.x, b.y);
14997         this.proxy.show();
14998         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14999                     true, .35, this.showEl.createDelegate(this));
15000     },
15001
15002     /**
15003      * Shows the dialog.
15004      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
15005      * @return {Roo.BasicDialog} this
15006      */
15007     show : function(animateTarget){
15008         if (this.fireEvent("beforeshow", this) === false){
15009             return;
15010         }
15011         if(this.syncHeightBeforeShow){
15012             this.syncBodyHeight();
15013         }else if(this.firstShow){
15014             this.firstShow = false;
15015             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
15016         }
15017         this.animateTarget = animateTarget || this.animateTarget;
15018         if(!this.el.isVisible()){
15019             this.beforeShow();
15020             if(this.animateTarget && Roo.get(this.animateTarget)){
15021                 this.animShow();
15022             }else{
15023                 this.showEl();
15024             }
15025         }
15026         return this;
15027     },
15028
15029     // private
15030     showEl : function(){
15031         this.proxy.hide();
15032         this.el.setXY(this.xy);
15033         this.el.show();
15034         this.adjustAssets(true);
15035         this.toFront();
15036         this.focus();
15037         // IE peekaboo bug - fix found by Dave Fenwick
15038         if(Roo.isIE){
15039             this.el.repaint();
15040         }
15041         this.fireEvent("show", this);
15042     },
15043
15044     /**
15045      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
15046      * dialog itself will receive focus.
15047      */
15048     focus : function(){
15049         if(this.defaultButton){
15050             this.defaultButton.focus();
15051         }else{
15052             this.focusEl.focus();
15053         }
15054     },
15055
15056     // private
15057     constrainXY : function(){
15058         if(this.constraintoviewport !== false){
15059             if(!this.viewSize){
15060                 if(this.container){
15061                     var s = this.container.getSize();
15062                     this.viewSize = [s.width, s.height];
15063                 }else{
15064                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
15065                 }
15066             }
15067             var s = Roo.get(this.container||document).getScroll();
15068
15069             var x = this.xy[0], y = this.xy[1];
15070             var w = this.size.width, h = this.size.height;
15071             var vw = this.viewSize[0], vh = this.viewSize[1];
15072             // only move it if it needs it
15073             var moved = false;
15074             // first validate right/bottom
15075             if(x + w > vw+s.left){
15076                 x = vw - w;
15077                 moved = true;
15078             }
15079             if(y + h > vh+s.top){
15080                 y = vh - h;
15081                 moved = true;
15082             }
15083             // then make sure top/left isn't negative
15084             if(x < s.left){
15085                 x = s.left;
15086                 moved = true;
15087             }
15088             if(y < s.top){
15089                 y = s.top;
15090                 moved = true;
15091             }
15092             if(moved){
15093                 // cache xy
15094                 this.xy = [x, y];
15095                 if(this.isVisible()){
15096                     this.el.setLocation(x, y);
15097                     this.adjustAssets();
15098                 }
15099             }
15100         }
15101     },
15102
15103     // private
15104     onDrag : function(){
15105         if(!this.proxyDrag){
15106             this.xy = this.el.getXY();
15107             this.adjustAssets();
15108         }
15109     },
15110
15111     // private
15112     adjustAssets : function(doShow){
15113         var x = this.xy[0], y = this.xy[1];
15114         var w = this.size.width, h = this.size.height;
15115         if(doShow === true){
15116             if(this.shadow){
15117                 this.shadow.show(this.el);
15118             }
15119             if(this.shim){
15120                 this.shim.show();
15121             }
15122         }
15123         if(this.shadow && this.shadow.isVisible()){
15124             this.shadow.show(this.el);
15125         }
15126         if(this.shim && this.shim.isVisible()){
15127             this.shim.setBounds(x, y, w, h);
15128         }
15129     },
15130
15131     // private
15132     adjustViewport : function(w, h){
15133         if(!w || !h){
15134             w = Roo.lib.Dom.getViewWidth();
15135             h = Roo.lib.Dom.getViewHeight();
15136         }
15137         // cache the size
15138         this.viewSize = [w, h];
15139         if(this.modal && this.mask.isVisible()){
15140             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15141             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15142         }
15143         if(this.isVisible()){
15144             this.constrainXY();
15145         }
15146     },
15147
15148     /**
15149      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15150      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15151      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15152      */
15153     destroy : function(removeEl){
15154         if(this.isVisible()){
15155             this.animateTarget = null;
15156             this.hide();
15157         }
15158         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15159         if(this.tabs){
15160             this.tabs.destroy(removeEl);
15161         }
15162         Roo.destroy(
15163              this.shim,
15164              this.proxy,
15165              this.resizer,
15166              this.close,
15167              this.mask
15168         );
15169         if(this.dd){
15170             this.dd.unreg();
15171         }
15172         if(this.buttons){
15173            for(var i = 0, len = this.buttons.length; i < len; i++){
15174                this.buttons[i].destroy();
15175            }
15176         }
15177         this.el.removeAllListeners();
15178         if(removeEl === true){
15179             this.el.update("");
15180             this.el.remove();
15181         }
15182         Roo.DialogManager.unregister(this);
15183     },
15184
15185     // private
15186     startMove : function(){
15187         if(this.proxyDrag){
15188             this.proxy.show();
15189         }
15190         if(this.constraintoviewport !== false){
15191             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15192         }
15193     },
15194
15195     // private
15196     endMove : function(){
15197         if(!this.proxyDrag){
15198             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15199         }else{
15200             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15201             this.proxy.hide();
15202         }
15203         this.refreshSize();
15204         this.adjustAssets();
15205         this.focus();
15206         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15207     },
15208
15209     /**
15210      * Brings this dialog to the front of any other visible dialogs
15211      * @return {Roo.BasicDialog} this
15212      */
15213     toFront : function(){
15214         Roo.DialogManager.bringToFront(this);
15215         return this;
15216     },
15217
15218     /**
15219      * Sends this dialog to the back (under) of any other visible dialogs
15220      * @return {Roo.BasicDialog} this
15221      */
15222     toBack : function(){
15223         Roo.DialogManager.sendToBack(this);
15224         return this;
15225     },
15226
15227     /**
15228      * Centers this dialog in the viewport
15229      * @return {Roo.BasicDialog} this
15230      */
15231     center : function(){
15232         var xy = this.el.getCenterXY(true);
15233         this.moveTo(xy[0], xy[1]);
15234         return this;
15235     },
15236
15237     /**
15238      * Moves the dialog's top-left corner to the specified point
15239      * @param {Number} x
15240      * @param {Number} y
15241      * @return {Roo.BasicDialog} this
15242      */
15243     moveTo : function(x, y){
15244         this.xy = [x,y];
15245         if(this.isVisible()){
15246             this.el.setXY(this.xy);
15247             this.adjustAssets();
15248         }
15249         return this;
15250     },
15251
15252     /**
15253      * Aligns the dialog to the specified element
15254      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15255      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15256      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15257      * @return {Roo.BasicDialog} this
15258      */
15259     alignTo : function(element, position, offsets){
15260         this.xy = this.el.getAlignToXY(element, position, offsets);
15261         if(this.isVisible()){
15262             this.el.setXY(this.xy);
15263             this.adjustAssets();
15264         }
15265         return this;
15266     },
15267
15268     /**
15269      * Anchors an element to another element and realigns it when the window is resized.
15270      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15271      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15272      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15273      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15274      * is a number, it is used as the buffer delay (defaults to 50ms).
15275      * @return {Roo.BasicDialog} this
15276      */
15277     anchorTo : function(el, alignment, offsets, monitorScroll){
15278         var action = function(){
15279             this.alignTo(el, alignment, offsets);
15280         };
15281         Roo.EventManager.onWindowResize(action, this);
15282         var tm = typeof monitorScroll;
15283         if(tm != 'undefined'){
15284             Roo.EventManager.on(window, 'scroll', action, this,
15285                 {buffer: tm == 'number' ? monitorScroll : 50});
15286         }
15287         action.call(this);
15288         return this;
15289     },
15290
15291     /**
15292      * Returns true if the dialog is visible
15293      * @return {Boolean}
15294      */
15295     isVisible : function(){
15296         return this.el.isVisible();
15297     },
15298
15299     // private
15300     animHide : function(callback){
15301         var b = Roo.get(this.animateTarget).getBox();
15302         this.proxy.show();
15303         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15304         this.el.hide();
15305         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15306                     this.hideEl.createDelegate(this, [callback]));
15307     },
15308
15309     /**
15310      * Hides the dialog.
15311      * @param {Function} callback (optional) Function to call when the dialog is hidden
15312      * @return {Roo.BasicDialog} this
15313      */
15314     hide : function(callback){
15315         if (this.fireEvent("beforehide", this) === false){
15316             return;
15317         }
15318         if(this.shadow){
15319             this.shadow.hide();
15320         }
15321         if(this.shim) {
15322           this.shim.hide();
15323         }
15324         // sometimes animateTarget seems to get set.. causing problems...
15325         // this just double checks..
15326         if(this.animateTarget && Roo.get(this.animateTarget)) {
15327            this.animHide(callback);
15328         }else{
15329             this.el.hide();
15330             this.hideEl(callback);
15331         }
15332         return this;
15333     },
15334
15335     // private
15336     hideEl : function(callback){
15337         this.proxy.hide();
15338         if(this.modal){
15339             this.mask.hide();
15340             Roo.get(document.body).removeClass("x-body-masked");
15341         }
15342         this.fireEvent("hide", this);
15343         if(typeof callback == "function"){
15344             callback();
15345         }
15346     },
15347
15348     // private
15349     hideAction : function(){
15350         this.setLeft("-10000px");
15351         this.setTop("-10000px");
15352         this.setStyle("visibility", "hidden");
15353     },
15354
15355     // private
15356     refreshSize : function(){
15357         this.size = this.el.getSize();
15358         this.xy = this.el.getXY();
15359         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15360     },
15361
15362     // private
15363     // z-index is managed by the DialogManager and may be overwritten at any time
15364     setZIndex : function(index){
15365         if(this.modal){
15366             this.mask.setStyle("z-index", index);
15367         }
15368         if(this.shim){
15369             this.shim.setStyle("z-index", ++index);
15370         }
15371         if(this.shadow){
15372             this.shadow.setZIndex(++index);
15373         }
15374         this.el.setStyle("z-index", ++index);
15375         if(this.proxy){
15376             this.proxy.setStyle("z-index", ++index);
15377         }
15378         if(this.resizer){
15379             this.resizer.proxy.setStyle("z-index", ++index);
15380         }
15381
15382         this.lastZIndex = index;
15383     },
15384
15385     /**
15386      * Returns the element for this dialog
15387      * @return {Roo.Element} The underlying dialog Element
15388      */
15389     getEl : function(){
15390         return this.el;
15391     }
15392 });
15393
15394 /**
15395  * @class Roo.DialogManager
15396  * Provides global access to BasicDialogs that have been created and
15397  * support for z-indexing (layering) multiple open dialogs.
15398  */
15399 Roo.DialogManager = function(){
15400     var list = {};
15401     var accessList = [];
15402     var front = null;
15403
15404     // private
15405     var sortDialogs = function(d1, d2){
15406         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15407     };
15408
15409     // private
15410     var orderDialogs = function(){
15411         accessList.sort(sortDialogs);
15412         var seed = Roo.DialogManager.zseed;
15413         for(var i = 0, len = accessList.length; i < len; i++){
15414             var dlg = accessList[i];
15415             if(dlg){
15416                 dlg.setZIndex(seed + (i*10));
15417             }
15418         }
15419     };
15420
15421     return {
15422         /**
15423          * The starting z-index for BasicDialogs (defaults to 9000)
15424          * @type Number The z-index value
15425          */
15426         zseed : 9000,
15427
15428         // private
15429         register : function(dlg){
15430             list[dlg.id] = dlg;
15431             accessList.push(dlg);
15432         },
15433
15434         // private
15435         unregister : function(dlg){
15436             delete list[dlg.id];
15437             var i=0;
15438             var len=0;
15439             if(!accessList.indexOf){
15440                 for(  i = 0, len = accessList.length; i < len; i++){
15441                     if(accessList[i] == dlg){
15442                         accessList.splice(i, 1);
15443                         return;
15444                     }
15445                 }
15446             }else{
15447                  i = accessList.indexOf(dlg);
15448                 if(i != -1){
15449                     accessList.splice(i, 1);
15450                 }
15451             }
15452         },
15453
15454         /**
15455          * Gets a registered dialog by id
15456          * @param {String/Object} id The id of the dialog or a dialog
15457          * @return {Roo.BasicDialog} this
15458          */
15459         get : function(id){
15460             return typeof id == "object" ? id : list[id];
15461         },
15462
15463         /**
15464          * Brings the specified dialog to the front
15465          * @param {String/Object} dlg The id of the dialog or a dialog
15466          * @return {Roo.BasicDialog} this
15467          */
15468         bringToFront : function(dlg){
15469             dlg = this.get(dlg);
15470             if(dlg != front){
15471                 front = dlg;
15472                 dlg._lastAccess = new Date().getTime();
15473                 orderDialogs();
15474             }
15475             return dlg;
15476         },
15477
15478         /**
15479          * Sends the specified dialog to the back
15480          * @param {String/Object} dlg The id of the dialog or a dialog
15481          * @return {Roo.BasicDialog} this
15482          */
15483         sendToBack : function(dlg){
15484             dlg = this.get(dlg);
15485             dlg._lastAccess = -(new Date().getTime());
15486             orderDialogs();
15487             return dlg;
15488         },
15489
15490         /**
15491          * Hides all dialogs
15492          */
15493         hideAll : function(){
15494             for(var id in list){
15495                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15496                     list[id].hide();
15497                 }
15498             }
15499         }
15500     };
15501 }();
15502
15503 /**
15504  * @class Roo.LayoutDialog
15505  * @extends Roo.BasicDialog
15506  * Dialog which provides adjustments for working with a layout in a Dialog.
15507  * Add your necessary layout config options to the dialog's config.<br>
15508  * Example usage (including a nested layout):
15509  * <pre><code>
15510 if(!dialog){
15511     dialog = new Roo.LayoutDialog("download-dlg", {
15512         modal: true,
15513         width:600,
15514         height:450,
15515         shadow:true,
15516         minWidth:500,
15517         minHeight:350,
15518         autoTabs:true,
15519         proxyDrag:true,
15520         // layout config merges with the dialog config
15521         center:{
15522             tabPosition: "top",
15523             alwaysShowTabs: true
15524         }
15525     });
15526     dialog.addKeyListener(27, dialog.hide, dialog);
15527     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15528     dialog.addButton("Build It!", this.getDownload, this);
15529
15530     // we can even add nested layouts
15531     var innerLayout = new Roo.BorderLayout("dl-inner", {
15532         east: {
15533             initialSize: 200,
15534             autoScroll:true,
15535             split:true
15536         },
15537         center: {
15538             autoScroll:true
15539         }
15540     });
15541     innerLayout.beginUpdate();
15542     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15543     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15544     innerLayout.endUpdate(true);
15545
15546     var layout = dialog.getLayout();
15547     layout.beginUpdate();
15548     layout.add("center", new Roo.ContentPanel("standard-panel",
15549                         {title: "Download the Source", fitToFrame:true}));
15550     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15551                {title: "Build your own roo.js"}));
15552     layout.getRegion("center").showPanel(sp);
15553     layout.endUpdate();
15554 }
15555 </code></pre>
15556     * @constructor
15557     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15558     * @param {Object} config configuration options
15559   */
15560 Roo.LayoutDialog = function(el, cfg){
15561     
15562     var config=  cfg;
15563     if (typeof(cfg) == 'undefined') {
15564         config = Roo.apply({}, el);
15565         // not sure why we use documentElement here.. - it should always be body.
15566         // IE7 borks horribly if we use documentElement.
15567         // webkit also does not like documentElement - it creates a body element...
15568         el = Roo.get( document.body || document.documentElement ).createChild();
15569         //config.autoCreate = true;
15570     }
15571     
15572     
15573     config.autoTabs = false;
15574     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15575     this.body.setStyle({overflow:"hidden", position:"relative"});
15576     this.layout = new Roo.BorderLayout(this.body.dom, config);
15577     this.layout.monitorWindowResize = false;
15578     this.el.addClass("x-dlg-auto-layout");
15579     // fix case when center region overwrites center function
15580     this.center = Roo.BasicDialog.prototype.center;
15581     this.on("show", this.layout.layout, this.layout, true);
15582     if (config.items) {
15583         var xitems = config.items;
15584         delete config.items;
15585         Roo.each(xitems, this.addxtype, this);
15586     }
15587     
15588     
15589 };
15590 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15591     /**
15592      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15593      * @deprecated
15594      */
15595     endUpdate : function(){
15596         this.layout.endUpdate();
15597     },
15598
15599     /**
15600      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15601      *  @deprecated
15602      */
15603     beginUpdate : function(){
15604         this.layout.beginUpdate();
15605     },
15606
15607     /**
15608      * Get the BorderLayout for this dialog
15609      * @return {Roo.BorderLayout}
15610      */
15611     getLayout : function(){
15612         return this.layout;
15613     },
15614
15615     showEl : function(){
15616         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15617         if(Roo.isIE7){
15618             this.layout.layout();
15619         }
15620     },
15621
15622     // private
15623     // Use the syncHeightBeforeShow config option to control this automatically
15624     syncBodyHeight : function(){
15625         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15626         if(this.layout){this.layout.layout();}
15627     },
15628     
15629       /**
15630      * Add an xtype element (actually adds to the layout.)
15631      * @return {Object} xdata xtype object data.
15632      */
15633     
15634     addxtype : function(c) {
15635         return this.layout.addxtype(c);
15636     }
15637 });/*
15638  * Based on:
15639  * Ext JS Library 1.1.1
15640  * Copyright(c) 2006-2007, Ext JS, LLC.
15641  *
15642  * Originally Released Under LGPL - original licence link has changed is not relivant.
15643  *
15644  * Fork - LGPL
15645  * <script type="text/javascript">
15646  */
15647  
15648 /**
15649  * @class Roo.MessageBox
15650  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15651  * Example usage:
15652  *<pre><code>
15653 // Basic alert:
15654 Roo.Msg.alert('Status', 'Changes saved successfully.');
15655
15656 // Prompt for user data:
15657 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15658     if (btn == 'ok'){
15659         // process text value...
15660     }
15661 });
15662
15663 // Show a dialog using config options:
15664 Roo.Msg.show({
15665    title:'Save Changes?',
15666    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15667    buttons: Roo.Msg.YESNOCANCEL,
15668    fn: processResult,
15669    animEl: 'elId'
15670 });
15671 </code></pre>
15672  * @singleton
15673  */
15674 Roo.MessageBox = function(){
15675     var dlg, opt, mask, waitTimer;
15676     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15677     var buttons, activeTextEl, bwidth;
15678
15679     // private
15680     var handleButton = function(button){
15681         dlg.hide();
15682         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15683     };
15684
15685     // private
15686     var handleHide = function(){
15687         if(opt && opt.cls){
15688             dlg.el.removeClass(opt.cls);
15689         }
15690         if(waitTimer){
15691             Roo.TaskMgr.stop(waitTimer);
15692             waitTimer = null;
15693         }
15694     };
15695
15696     // private
15697     var updateButtons = function(b){
15698         var width = 0;
15699         if(!b){
15700             buttons["ok"].hide();
15701             buttons["cancel"].hide();
15702             buttons["yes"].hide();
15703             buttons["no"].hide();
15704             dlg.footer.dom.style.display = 'none';
15705             return width;
15706         }
15707         dlg.footer.dom.style.display = '';
15708         for(var k in buttons){
15709             if(typeof buttons[k] != "function"){
15710                 if(b[k]){
15711                     buttons[k].show();
15712                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15713                     width += buttons[k].el.getWidth()+15;
15714                 }else{
15715                     buttons[k].hide();
15716                 }
15717             }
15718         }
15719         return width;
15720     };
15721
15722     // private
15723     var handleEsc = function(d, k, e){
15724         if(opt && opt.closable !== false){
15725             dlg.hide();
15726         }
15727         if(e){
15728             e.stopEvent();
15729         }
15730     };
15731
15732     return {
15733         /**
15734          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15735          * @return {Roo.BasicDialog} The BasicDialog element
15736          */
15737         getDialog : function(){
15738            if(!dlg){
15739                 dlg = new Roo.BasicDialog("x-msg-box", {
15740                     autoCreate : true,
15741                     shadow: true,
15742                     draggable: true,
15743                     resizable:false,
15744                     constraintoviewport:false,
15745                     fixedcenter:true,
15746                     collapsible : false,
15747                     shim:true,
15748                     modal: true,
15749                     width:400, height:100,
15750                     buttonAlign:"center",
15751                     closeClick : function(){
15752                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15753                             handleButton("no");
15754                         }else{
15755                             handleButton("cancel");
15756                         }
15757                     }
15758                 });
15759                 dlg.on("hide", handleHide);
15760                 mask = dlg.mask;
15761                 dlg.addKeyListener(27, handleEsc);
15762                 buttons = {};
15763                 var bt = this.buttonText;
15764                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15765                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15766                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15767                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15768                 bodyEl = dlg.body.createChild({
15769
15770                     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>'
15771                 });
15772                 msgEl = bodyEl.dom.firstChild;
15773                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15774                 textboxEl.enableDisplayMode();
15775                 textboxEl.addKeyListener([10,13], function(){
15776                     if(dlg.isVisible() && opt && opt.buttons){
15777                         if(opt.buttons.ok){
15778                             handleButton("ok");
15779                         }else if(opt.buttons.yes){
15780                             handleButton("yes");
15781                         }
15782                     }
15783                 });
15784                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15785                 textareaEl.enableDisplayMode();
15786                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15787                 progressEl.enableDisplayMode();
15788                 var pf = progressEl.dom.firstChild;
15789                 if (pf) {
15790                     pp = Roo.get(pf.firstChild);
15791                     pp.setHeight(pf.offsetHeight);
15792                 }
15793                 
15794             }
15795             return dlg;
15796         },
15797
15798         /**
15799          * Updates the message box body text
15800          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15801          * the XHTML-compliant non-breaking space character '&amp;#160;')
15802          * @return {Roo.MessageBox} This message box
15803          */
15804         updateText : function(text){
15805             if(!dlg.isVisible() && !opt.width){
15806                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15807             }
15808             msgEl.innerHTML = text || '&#160;';
15809       
15810             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15811             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15812             var w = Math.max(
15813                     Math.min(opt.width || cw , this.maxWidth), 
15814                     Math.max(opt.minWidth || this.minWidth, bwidth)
15815             );
15816             if(opt.prompt){
15817                 activeTextEl.setWidth(w);
15818             }
15819             if(dlg.isVisible()){
15820                 dlg.fixedcenter = false;
15821             }
15822             // to big, make it scroll. = But as usual stupid IE does not support
15823             // !important..
15824             
15825             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15826                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15827                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15828             } else {
15829                 bodyEl.dom.style.height = '';
15830                 bodyEl.dom.style.overflowY = '';
15831             }
15832             if (cw > w) {
15833                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15834             } else {
15835                 bodyEl.dom.style.overflowX = '';
15836             }
15837             
15838             dlg.setContentSize(w, bodyEl.getHeight());
15839             if(dlg.isVisible()){
15840                 dlg.fixedcenter = true;
15841             }
15842             return this;
15843         },
15844
15845         /**
15846          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15847          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15848          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15849          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15850          * @return {Roo.MessageBox} This message box
15851          */
15852         updateProgress : function(value, text){
15853             if(text){
15854                 this.updateText(text);
15855             }
15856             if (pp) { // weird bug on my firefox - for some reason this is not defined
15857                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15858             }
15859             return this;
15860         },        
15861
15862         /**
15863          * Returns true if the message box is currently displayed
15864          * @return {Boolean} True if the message box is visible, else false
15865          */
15866         isVisible : function(){
15867             return dlg && dlg.isVisible();  
15868         },
15869
15870         /**
15871          * Hides the message box if it is displayed
15872          */
15873         hide : function(){
15874             if(this.isVisible()){
15875                 dlg.hide();
15876             }  
15877         },
15878
15879         /**
15880          * Displays a new message box, or reinitializes an existing message box, based on the config options
15881          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15882          * The following config object properties are supported:
15883          * <pre>
15884 Property    Type             Description
15885 ----------  ---------------  ------------------------------------------------------------------------------------
15886 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15887                                    closes (defaults to undefined)
15888 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15889                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15890 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15891                                    progress and wait dialogs will ignore this property and always hide the
15892                                    close button as they can only be closed programmatically.
15893 cls               String           A custom CSS class to apply to the message box element
15894 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15895                                    displayed (defaults to 75)
15896 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15897                                    function will be btn (the name of the button that was clicked, if applicable,
15898                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15899                                    Progress and wait dialogs will ignore this option since they do not respond to
15900                                    user actions and can only be closed programmatically, so any required function
15901                                    should be called by the same code after it closes the dialog.
15902 icon              String           A CSS class that provides a background image to be used as an icon for
15903                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15904 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15905 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15906 modal             Boolean          False to allow user interaction with the page while the message box is
15907                                    displayed (defaults to true)
15908 msg               String           A string that will replace the existing message box body text (defaults
15909                                    to the XHTML-compliant non-breaking space character '&#160;')
15910 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15911 progress          Boolean          True to display a progress bar (defaults to false)
15912 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15913 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15914 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15915 title             String           The title text
15916 value             String           The string value to set into the active textbox element if displayed
15917 wait              Boolean          True to display a progress bar (defaults to false)
15918 width             Number           The width of the dialog in pixels
15919 </pre>
15920          *
15921          * Example usage:
15922          * <pre><code>
15923 Roo.Msg.show({
15924    title: 'Address',
15925    msg: 'Please enter your address:',
15926    width: 300,
15927    buttons: Roo.MessageBox.OKCANCEL,
15928    multiline: true,
15929    fn: saveAddress,
15930    animEl: 'addAddressBtn'
15931 });
15932 </code></pre>
15933          * @param {Object} config Configuration options
15934          * @return {Roo.MessageBox} This message box
15935          */
15936         show : function(options)
15937         {
15938             
15939             // this causes nightmares if you show one dialog after another
15940             // especially on callbacks..
15941              
15942             if(this.isVisible()){
15943                 
15944                 this.hide();
15945                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15946                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15947                 Roo.log("New Dialog Message:" +  options.msg )
15948                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15949                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15950                 
15951             }
15952             var d = this.getDialog();
15953             opt = options;
15954             d.setTitle(opt.title || "&#160;");
15955             d.close.setDisplayed(opt.closable !== false);
15956             activeTextEl = textboxEl;
15957             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15958             if(opt.prompt){
15959                 if(opt.multiline){
15960                     textboxEl.hide();
15961                     textareaEl.show();
15962                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15963                         opt.multiline : this.defaultTextHeight);
15964                     activeTextEl = textareaEl;
15965                 }else{
15966                     textboxEl.show();
15967                     textareaEl.hide();
15968                 }
15969             }else{
15970                 textboxEl.hide();
15971                 textareaEl.hide();
15972             }
15973             progressEl.setDisplayed(opt.progress === true);
15974             this.updateProgress(0);
15975             activeTextEl.dom.value = opt.value || "";
15976             if(opt.prompt){
15977                 dlg.setDefaultButton(activeTextEl);
15978             }else{
15979                 var bs = opt.buttons;
15980                 var db = null;
15981                 if(bs && bs.ok){
15982                     db = buttons["ok"];
15983                 }else if(bs && bs.yes){
15984                     db = buttons["yes"];
15985                 }
15986                 dlg.setDefaultButton(db);
15987             }
15988             bwidth = updateButtons(opt.buttons);
15989             this.updateText(opt.msg);
15990             if(opt.cls){
15991                 d.el.addClass(opt.cls);
15992             }
15993             d.proxyDrag = opt.proxyDrag === true;
15994             d.modal = opt.modal !== false;
15995             d.mask = opt.modal !== false ? mask : false;
15996             if(!d.isVisible()){
15997                 // force it to the end of the z-index stack so it gets a cursor in FF
15998                 document.body.appendChild(dlg.el.dom);
15999                 d.animateTarget = null;
16000                 d.show(options.animEl);
16001             }
16002             return this;
16003         },
16004
16005         /**
16006          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
16007          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
16008          * and closing the message box when the process is complete.
16009          * @param {String} title The title bar text
16010          * @param {String} msg The message box body text
16011          * @return {Roo.MessageBox} This message box
16012          */
16013         progress : function(title, msg){
16014             this.show({
16015                 title : title,
16016                 msg : msg,
16017                 buttons: false,
16018                 progress:true,
16019                 closable:false,
16020                 minWidth: this.minProgressWidth,
16021                 modal : true
16022             });
16023             return this;
16024         },
16025
16026         /**
16027          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
16028          * If a callback function is passed it will be called after the user clicks the button, and the
16029          * id of the button that was clicked will be passed as the only parameter to the callback
16030          * (could also be the top-right close button).
16031          * @param {String} title The title bar text
16032          * @param {String} msg The message box body text
16033          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16034          * @param {Object} scope (optional) The scope of the callback function
16035          * @return {Roo.MessageBox} This message box
16036          */
16037         alert : function(title, msg, fn, scope){
16038             this.show({
16039                 title : title,
16040                 msg : msg,
16041                 buttons: this.OK,
16042                 fn: fn,
16043                 scope : scope,
16044                 modal : true
16045             });
16046             return this;
16047         },
16048
16049         /**
16050          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
16051          * interaction while waiting for a long-running process to complete that does not have defined intervals.
16052          * You are responsible for closing the message box when the process is complete.
16053          * @param {String} msg The message box body text
16054          * @param {String} title (optional) The title bar text
16055          * @return {Roo.MessageBox} This message box
16056          */
16057         wait : function(msg, title){
16058             this.show({
16059                 title : title,
16060                 msg : msg,
16061                 buttons: false,
16062                 closable:false,
16063                 progress:true,
16064                 modal:true,
16065                 width:300,
16066                 wait:true
16067             });
16068             waitTimer = Roo.TaskMgr.start({
16069                 run: function(i){
16070                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
16071                 },
16072                 interval: 1000
16073             });
16074             return this;
16075         },
16076
16077         /**
16078          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
16079          * If a callback function is passed it will be called after the user clicks either button, and the id of the
16080          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
16081          * @param {String} title The title bar text
16082          * @param {String} msg The message box body text
16083          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16084          * @param {Object} scope (optional) The scope of the callback function
16085          * @return {Roo.MessageBox} This message box
16086          */
16087         confirm : function(title, msg, fn, scope){
16088             this.show({
16089                 title : title,
16090                 msg : msg,
16091                 buttons: this.YESNO,
16092                 fn: fn,
16093                 scope : scope,
16094                 modal : true
16095             });
16096             return this;
16097         },
16098
16099         /**
16100          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
16101          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
16102          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
16103          * (could also be the top-right close button) and the text that was entered will be passed as the two
16104          * parameters to the callback.
16105          * @param {String} title The title bar text
16106          * @param {String} msg The message box body text
16107          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16108          * @param {Object} scope (optional) The scope of the callback function
16109          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16110          * property, or the height in pixels to create the textbox (defaults to false / single-line)
16111          * @return {Roo.MessageBox} This message box
16112          */
16113         prompt : function(title, msg, fn, scope, multiline){
16114             this.show({
16115                 title : title,
16116                 msg : msg,
16117                 buttons: this.OKCANCEL,
16118                 fn: fn,
16119                 minWidth:250,
16120                 scope : scope,
16121                 prompt:true,
16122                 multiline: multiline,
16123                 modal : true
16124             });
16125             return this;
16126         },
16127
16128         /**
16129          * Button config that displays a single OK button
16130          * @type Object
16131          */
16132         OK : {ok:true},
16133         /**
16134          * Button config that displays Yes and No buttons
16135          * @type Object
16136          */
16137         YESNO : {yes:true, no:true},
16138         /**
16139          * Button config that displays OK and Cancel buttons
16140          * @type Object
16141          */
16142         OKCANCEL : {ok:true, cancel:true},
16143         /**
16144          * Button config that displays Yes, No and Cancel buttons
16145          * @type Object
16146          */
16147         YESNOCANCEL : {yes:true, no:true, cancel:true},
16148
16149         /**
16150          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16151          * @type Number
16152          */
16153         defaultTextHeight : 75,
16154         /**
16155          * The maximum width in pixels of the message box (defaults to 600)
16156          * @type Number
16157          */
16158         maxWidth : 600,
16159         /**
16160          * The minimum width in pixels of the message box (defaults to 100)
16161          * @type Number
16162          */
16163         minWidth : 100,
16164         /**
16165          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16166          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16167          * @type Number
16168          */
16169         minProgressWidth : 250,
16170         /**
16171          * An object containing the default button text strings that can be overriden for localized language support.
16172          * Supported properties are: ok, cancel, yes and no.
16173          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16174          * @type Object
16175          */
16176         buttonText : {
16177             ok : "OK",
16178             cancel : "Cancel",
16179             yes : "Yes",
16180             no : "No"
16181         }
16182     };
16183 }();
16184
16185 /**
16186  * Shorthand for {@link Roo.MessageBox}
16187  */
16188 Roo.Msg = Roo.MessageBox;/*
16189  * Based on:
16190  * Ext JS Library 1.1.1
16191  * Copyright(c) 2006-2007, Ext JS, LLC.
16192  *
16193  * Originally Released Under LGPL - original licence link has changed is not relivant.
16194  *
16195  * Fork - LGPL
16196  * <script type="text/javascript">
16197  */
16198 /**
16199  * @class Roo.QuickTips
16200  * Provides attractive and customizable tooltips for any element.
16201  * @singleton
16202  */
16203 Roo.QuickTips = function(){
16204     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16205     var ce, bd, xy, dd;
16206     var visible = false, disabled = true, inited = false;
16207     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16208     
16209     var onOver = function(e){
16210         if(disabled){
16211             return;
16212         }
16213         var t = e.getTarget();
16214         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16215             return;
16216         }
16217         if(ce && t == ce.el){
16218             clearTimeout(hideProc);
16219             return;
16220         }
16221         if(t && tagEls[t.id]){
16222             tagEls[t.id].el = t;
16223             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16224             return;
16225         }
16226         var ttp, et = Roo.fly(t);
16227         var ns = cfg.namespace;
16228         if(tm.interceptTitles && t.title){
16229             ttp = t.title;
16230             t.qtip = ttp;
16231             t.removeAttribute("title");
16232             e.preventDefault();
16233         }else{
16234             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16235         }
16236         if(ttp){
16237             showProc = show.defer(tm.showDelay, tm, [{
16238                 el: t, 
16239                 text: ttp, 
16240                 width: et.getAttributeNS(ns, cfg.width),
16241                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16242                 title: et.getAttributeNS(ns, cfg.title),
16243                     cls: et.getAttributeNS(ns, cfg.cls)
16244             }]);
16245         }
16246     };
16247     
16248     var onOut = function(e){
16249         clearTimeout(showProc);
16250         var t = e.getTarget();
16251         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16252             hideProc = setTimeout(hide, tm.hideDelay);
16253         }
16254     };
16255     
16256     var onMove = function(e){
16257         if(disabled){
16258             return;
16259         }
16260         xy = e.getXY();
16261         xy[1] += 18;
16262         if(tm.trackMouse && ce){
16263             el.setXY(xy);
16264         }
16265     };
16266     
16267     var onDown = function(e){
16268         clearTimeout(showProc);
16269         clearTimeout(hideProc);
16270         if(!e.within(el)){
16271             if(tm.hideOnClick){
16272                 hide();
16273                 tm.disable();
16274                 tm.enable.defer(100, tm);
16275             }
16276         }
16277     };
16278     
16279     var getPad = function(){
16280         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16281     };
16282
16283     var show = function(o){
16284         if(disabled){
16285             return;
16286         }
16287         clearTimeout(dismissProc);
16288         ce = o;
16289         if(removeCls){ // in case manually hidden
16290             el.removeClass(removeCls);
16291             removeCls = null;
16292         }
16293         if(ce.cls){
16294             el.addClass(ce.cls);
16295             removeCls = ce.cls;
16296         }
16297         if(ce.title){
16298             tipTitle.update(ce.title);
16299             tipTitle.show();
16300         }else{
16301             tipTitle.update('');
16302             tipTitle.hide();
16303         }
16304         el.dom.style.width  = tm.maxWidth+'px';
16305         //tipBody.dom.style.width = '';
16306         tipBodyText.update(o.text);
16307         var p = getPad(), w = ce.width;
16308         if(!w){
16309             var td = tipBodyText.dom;
16310             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16311             if(aw > tm.maxWidth){
16312                 w = tm.maxWidth;
16313             }else if(aw < tm.minWidth){
16314                 w = tm.minWidth;
16315             }else{
16316                 w = aw;
16317             }
16318         }
16319         //tipBody.setWidth(w);
16320         el.setWidth(parseInt(w, 10) + p);
16321         if(ce.autoHide === false){
16322             close.setDisplayed(true);
16323             if(dd){
16324                 dd.unlock();
16325             }
16326         }else{
16327             close.setDisplayed(false);
16328             if(dd){
16329                 dd.lock();
16330             }
16331         }
16332         if(xy){
16333             el.avoidY = xy[1]-18;
16334             el.setXY(xy);
16335         }
16336         if(tm.animate){
16337             el.setOpacity(.1);
16338             el.setStyle("visibility", "visible");
16339             el.fadeIn({callback: afterShow});
16340         }else{
16341             afterShow();
16342         }
16343     };
16344     
16345     var afterShow = function(){
16346         if(ce){
16347             el.show();
16348             esc.enable();
16349             if(tm.autoDismiss && ce.autoHide !== false){
16350                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16351             }
16352         }
16353     };
16354     
16355     var hide = function(noanim){
16356         clearTimeout(dismissProc);
16357         clearTimeout(hideProc);
16358         ce = null;
16359         if(el.isVisible()){
16360             esc.disable();
16361             if(noanim !== true && tm.animate){
16362                 el.fadeOut({callback: afterHide});
16363             }else{
16364                 afterHide();
16365             } 
16366         }
16367     };
16368     
16369     var afterHide = function(){
16370         el.hide();
16371         if(removeCls){
16372             el.removeClass(removeCls);
16373             removeCls = null;
16374         }
16375     };
16376     
16377     return {
16378         /**
16379         * @cfg {Number} minWidth
16380         * The minimum width of the quick tip (defaults to 40)
16381         */
16382        minWidth : 40,
16383         /**
16384         * @cfg {Number} maxWidth
16385         * The maximum width of the quick tip (defaults to 300)
16386         */
16387        maxWidth : 300,
16388         /**
16389         * @cfg {Boolean} interceptTitles
16390         * True to automatically use the element's DOM title value if available (defaults to false)
16391         */
16392        interceptTitles : false,
16393         /**
16394         * @cfg {Boolean} trackMouse
16395         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16396         */
16397        trackMouse : false,
16398         /**
16399         * @cfg {Boolean} hideOnClick
16400         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16401         */
16402        hideOnClick : true,
16403         /**
16404         * @cfg {Number} showDelay
16405         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16406         */
16407        showDelay : 500,
16408         /**
16409         * @cfg {Number} hideDelay
16410         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16411         */
16412        hideDelay : 200,
16413         /**
16414         * @cfg {Boolean} autoHide
16415         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16416         * Used in conjunction with hideDelay.
16417         */
16418        autoHide : true,
16419         /**
16420         * @cfg {Boolean}
16421         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16422         * (defaults to true).  Used in conjunction with autoDismissDelay.
16423         */
16424        autoDismiss : true,
16425         /**
16426         * @cfg {Number}
16427         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16428         */
16429        autoDismissDelay : 5000,
16430        /**
16431         * @cfg {Boolean} animate
16432         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16433         */
16434        animate : false,
16435
16436        /**
16437         * @cfg {String} title
16438         * Title text to display (defaults to '').  This can be any valid HTML markup.
16439         */
16440         title: '',
16441        /**
16442         * @cfg {String} text
16443         * Body text to display (defaults to '').  This can be any valid HTML markup.
16444         */
16445         text : '',
16446        /**
16447         * @cfg {String} cls
16448         * A CSS class to apply to the base quick tip element (defaults to '').
16449         */
16450         cls : '',
16451        /**
16452         * @cfg {Number} width
16453         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16454         * minWidth or maxWidth.
16455         */
16456         width : null,
16457
16458     /**
16459      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16460      * or display QuickTips in a page.
16461      */
16462        init : function(){
16463           tm = Roo.QuickTips;
16464           cfg = tm.tagConfig;
16465           if(!inited){
16466               if(!Roo.isReady){ // allow calling of init() before onReady
16467                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16468                   return;
16469               }
16470               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16471               el.fxDefaults = {stopFx: true};
16472               // maximum custom styling
16473               //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>');
16474               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>');              
16475               tipTitle = el.child('h3');
16476               tipTitle.enableDisplayMode("block");
16477               tipBody = el.child('div.x-tip-bd');
16478               tipBodyText = el.child('div.x-tip-bd-inner');
16479               //bdLeft = el.child('div.x-tip-bd-left');
16480               //bdRight = el.child('div.x-tip-bd-right');
16481               close = el.child('div.x-tip-close');
16482               close.enableDisplayMode("block");
16483               close.on("click", hide);
16484               var d = Roo.get(document);
16485               d.on("mousedown", onDown);
16486               d.on("mouseover", onOver);
16487               d.on("mouseout", onOut);
16488               d.on("mousemove", onMove);
16489               esc = d.addKeyListener(27, hide);
16490               esc.disable();
16491               if(Roo.dd.DD){
16492                   dd = el.initDD("default", null, {
16493                       onDrag : function(){
16494                           el.sync();  
16495                       }
16496                   });
16497                   dd.setHandleElId(tipTitle.id);
16498                   dd.lock();
16499               }
16500               inited = true;
16501           }
16502           this.enable(); 
16503        },
16504
16505     /**
16506      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16507      * are supported:
16508      * <pre>
16509 Property    Type                   Description
16510 ----------  ---------------------  ------------------------------------------------------------------------
16511 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16512      * </ul>
16513      * @param {Object} config The config object
16514      */
16515        register : function(config){
16516            var cs = config instanceof Array ? config : arguments;
16517            for(var i = 0, len = cs.length; i < len; i++) {
16518                var c = cs[i];
16519                var target = c.target;
16520                if(target){
16521                    if(target instanceof Array){
16522                        for(var j = 0, jlen = target.length; j < jlen; j++){
16523                            tagEls[target[j]] = c;
16524                        }
16525                    }else{
16526                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16527                    }
16528                }
16529            }
16530        },
16531
16532     /**
16533      * Removes this quick tip from its element and destroys it.
16534      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16535      */
16536        unregister : function(el){
16537            delete tagEls[Roo.id(el)];
16538        },
16539
16540     /**
16541      * Enable this quick tip.
16542      */
16543        enable : function(){
16544            if(inited && disabled){
16545                locks.pop();
16546                if(locks.length < 1){
16547                    disabled = false;
16548                }
16549            }
16550        },
16551
16552     /**
16553      * Disable this quick tip.
16554      */
16555        disable : function(){
16556           disabled = true;
16557           clearTimeout(showProc);
16558           clearTimeout(hideProc);
16559           clearTimeout(dismissProc);
16560           if(ce){
16561               hide(true);
16562           }
16563           locks.push(1);
16564        },
16565
16566     /**
16567      * Returns true if the quick tip is enabled, else false.
16568      */
16569        isEnabled : function(){
16570             return !disabled;
16571        },
16572
16573         // private
16574        tagConfig : {
16575            namespace : "ext",
16576            attribute : "qtip",
16577            width : "width",
16578            target : "target",
16579            title : "qtitle",
16580            hide : "hide",
16581            cls : "qclass"
16582        }
16583    };
16584 }();
16585
16586 // backwards compat
16587 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16588  * Based on:
16589  * Ext JS Library 1.1.1
16590  * Copyright(c) 2006-2007, Ext JS, LLC.
16591  *
16592  * Originally Released Under LGPL - original licence link has changed is not relivant.
16593  *
16594  * Fork - LGPL
16595  * <script type="text/javascript">
16596  */
16597  
16598
16599 /**
16600  * @class Roo.tree.TreePanel
16601  * @extends Roo.data.Tree
16602
16603  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16604  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16605  * @cfg {Boolean} enableDD true to enable drag and drop
16606  * @cfg {Boolean} enableDrag true to enable just drag
16607  * @cfg {Boolean} enableDrop true to enable just drop
16608  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16609  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16610  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16611  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16612  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16613  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16614  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16615  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16616  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16617  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16618  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16619  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16620  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16621  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16622  * @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>
16623  * @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>
16624  * 
16625  * @constructor
16626  * @param {String/HTMLElement/Element} el The container element
16627  * @param {Object} config
16628  */
16629 Roo.tree.TreePanel = function(el, config){
16630     var root = false;
16631     var loader = false;
16632     if (config.root) {
16633         root = config.root;
16634         delete config.root;
16635     }
16636     if (config.loader) {
16637         loader = config.loader;
16638         delete config.loader;
16639     }
16640     
16641     Roo.apply(this, config);
16642     Roo.tree.TreePanel.superclass.constructor.call(this);
16643     this.el = Roo.get(el);
16644     this.el.addClass('x-tree');
16645     //console.log(root);
16646     if (root) {
16647         this.setRootNode( Roo.factory(root, Roo.tree));
16648     }
16649     if (loader) {
16650         this.loader = Roo.factory(loader, Roo.tree);
16651     }
16652    /**
16653     * Read-only. The id of the container element becomes this TreePanel's id.
16654     */
16655     this.id = this.el.id;
16656     this.addEvents({
16657         /**
16658         * @event beforeload
16659         * Fires before a node is loaded, return false to cancel
16660         * @param {Node} node The node being loaded
16661         */
16662         "beforeload" : true,
16663         /**
16664         * @event load
16665         * Fires when a node is loaded
16666         * @param {Node} node The node that was loaded
16667         */
16668         "load" : true,
16669         /**
16670         * @event textchange
16671         * Fires when the text for a node is changed
16672         * @param {Node} node The node
16673         * @param {String} text The new text
16674         * @param {String} oldText The old text
16675         */
16676         "textchange" : true,
16677         /**
16678         * @event beforeexpand
16679         * Fires before a node is expanded, return false to cancel.
16680         * @param {Node} node The node
16681         * @param {Boolean} deep
16682         * @param {Boolean} anim
16683         */
16684         "beforeexpand" : true,
16685         /**
16686         * @event beforecollapse
16687         * Fires before a node is collapsed, return false to cancel.
16688         * @param {Node} node The node
16689         * @param {Boolean} deep
16690         * @param {Boolean} anim
16691         */
16692         "beforecollapse" : true,
16693         /**
16694         * @event expand
16695         * Fires when a node is expanded
16696         * @param {Node} node The node
16697         */
16698         "expand" : true,
16699         /**
16700         * @event disabledchange
16701         * Fires when the disabled status of a node changes
16702         * @param {Node} node The node
16703         * @param {Boolean} disabled
16704         */
16705         "disabledchange" : true,
16706         /**
16707         * @event collapse
16708         * Fires when a node is collapsed
16709         * @param {Node} node The node
16710         */
16711         "collapse" : true,
16712         /**
16713         * @event beforeclick
16714         * Fires before click processing on a node. Return false to cancel the default action.
16715         * @param {Node} node The node
16716         * @param {Roo.EventObject} e The event object
16717         */
16718         "beforeclick":true,
16719         /**
16720         * @event checkchange
16721         * Fires when a node with a checkbox's checked property changes
16722         * @param {Node} this This node
16723         * @param {Boolean} checked
16724         */
16725         "checkchange":true,
16726         /**
16727         * @event click
16728         * Fires when a node is clicked
16729         * @param {Node} node The node
16730         * @param {Roo.EventObject} e The event object
16731         */
16732         "click":true,
16733         /**
16734         * @event dblclick
16735         * Fires when a node is double clicked
16736         * @param {Node} node The node
16737         * @param {Roo.EventObject} e The event object
16738         */
16739         "dblclick":true,
16740         /**
16741         * @event contextmenu
16742         * Fires when a node is right clicked
16743         * @param {Node} node The node
16744         * @param {Roo.EventObject} e The event object
16745         */
16746         "contextmenu":true,
16747         /**
16748         * @event beforechildrenrendered
16749         * Fires right before the child nodes for a node are rendered
16750         * @param {Node} node The node
16751         */
16752         "beforechildrenrendered":true,
16753         /**
16754         * @event startdrag
16755         * Fires when a node starts being dragged
16756         * @param {Roo.tree.TreePanel} this
16757         * @param {Roo.tree.TreeNode} node
16758         * @param {event} e The raw browser event
16759         */ 
16760        "startdrag" : true,
16761        /**
16762         * @event enddrag
16763         * Fires when a drag operation is complete
16764         * @param {Roo.tree.TreePanel} this
16765         * @param {Roo.tree.TreeNode} node
16766         * @param {event} e The raw browser event
16767         */
16768        "enddrag" : true,
16769        /**
16770         * @event dragdrop
16771         * Fires when a dragged node is dropped on a valid DD target
16772         * @param {Roo.tree.TreePanel} this
16773         * @param {Roo.tree.TreeNode} node
16774         * @param {DD} dd The dd it was dropped on
16775         * @param {event} e The raw browser event
16776         */
16777        "dragdrop" : true,
16778        /**
16779         * @event beforenodedrop
16780         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16781         * passed to handlers has the following properties:<br />
16782         * <ul style="padding:5px;padding-left:16px;">
16783         * <li>tree - The TreePanel</li>
16784         * <li>target - The node being targeted for the drop</li>
16785         * <li>data - The drag data from the drag source</li>
16786         * <li>point - The point of the drop - append, above or below</li>
16787         * <li>source - The drag source</li>
16788         * <li>rawEvent - Raw mouse event</li>
16789         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16790         * to be inserted by setting them on this object.</li>
16791         * <li>cancel - Set this to true to cancel the drop.</li>
16792         * </ul>
16793         * @param {Object} dropEvent
16794         */
16795        "beforenodedrop" : true,
16796        /**
16797         * @event nodedrop
16798         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16799         * passed to handlers has the following properties:<br />
16800         * <ul style="padding:5px;padding-left:16px;">
16801         * <li>tree - The TreePanel</li>
16802         * <li>target - The node being targeted for the drop</li>
16803         * <li>data - The drag data from the drag source</li>
16804         * <li>point - The point of the drop - append, above or below</li>
16805         * <li>source - The drag source</li>
16806         * <li>rawEvent - Raw mouse event</li>
16807         * <li>dropNode - Dropped node(s).</li>
16808         * </ul>
16809         * @param {Object} dropEvent
16810         */
16811        "nodedrop" : true,
16812         /**
16813         * @event nodedragover
16814         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16815         * passed to handlers has the following properties:<br />
16816         * <ul style="padding:5px;padding-left:16px;">
16817         * <li>tree - The TreePanel</li>
16818         * <li>target - The node being targeted for the drop</li>
16819         * <li>data - The drag data from the drag source</li>
16820         * <li>point - The point of the drop - append, above or below</li>
16821         * <li>source - The drag source</li>
16822         * <li>rawEvent - Raw mouse event</li>
16823         * <li>dropNode - Drop node(s) provided by the source.</li>
16824         * <li>cancel - Set this to true to signal drop not allowed.</li>
16825         * </ul>
16826         * @param {Object} dragOverEvent
16827         */
16828        "nodedragover" : true
16829         
16830     });
16831     if(this.singleExpand){
16832        this.on("beforeexpand", this.restrictExpand, this);
16833     }
16834     if (this.editor) {
16835         this.editor.tree = this;
16836         this.editor = Roo.factory(this.editor, Roo.tree);
16837     }
16838     
16839     if (this.selModel) {
16840         this.selModel = Roo.factory(this.selModel, Roo.tree);
16841     }
16842    
16843 };
16844 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16845     rootVisible : true,
16846     animate: Roo.enableFx,
16847     lines : true,
16848     enableDD : false,
16849     hlDrop : Roo.enableFx,
16850   
16851     renderer: false,
16852     
16853     rendererTip: false,
16854     // private
16855     restrictExpand : function(node){
16856         var p = node.parentNode;
16857         if(p){
16858             if(p.expandedChild && p.expandedChild.parentNode == p){
16859                 p.expandedChild.collapse();
16860             }
16861             p.expandedChild = node;
16862         }
16863     },
16864
16865     // private override
16866     setRootNode : function(node){
16867         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16868         if(!this.rootVisible){
16869             node.ui = new Roo.tree.RootTreeNodeUI(node);
16870         }
16871         return node;
16872     },
16873
16874     /**
16875      * Returns the container element for this TreePanel
16876      */
16877     getEl : function(){
16878         return this.el;
16879     },
16880
16881     /**
16882      * Returns the default TreeLoader for this TreePanel
16883      */
16884     getLoader : function(){
16885         return this.loader;
16886     },
16887
16888     /**
16889      * Expand all nodes
16890      */
16891     expandAll : function(){
16892         this.root.expand(true);
16893     },
16894
16895     /**
16896      * Collapse all nodes
16897      */
16898     collapseAll : function(){
16899         this.root.collapse(true);
16900     },
16901
16902     /**
16903      * Returns the selection model used by this TreePanel
16904      */
16905     getSelectionModel : function(){
16906         if(!this.selModel){
16907             this.selModel = new Roo.tree.DefaultSelectionModel();
16908         }
16909         return this.selModel;
16910     },
16911
16912     /**
16913      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16914      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16915      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16916      * @return {Array}
16917      */
16918     getChecked : function(a, startNode){
16919         startNode = startNode || this.root;
16920         var r = [];
16921         var f = function(){
16922             if(this.attributes.checked){
16923                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16924             }
16925         }
16926         startNode.cascade(f);
16927         return r;
16928     },
16929
16930     /**
16931      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16932      * @param {String} path
16933      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16934      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16935      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16936      */
16937     expandPath : function(path, attr, callback){
16938         attr = attr || "id";
16939         var keys = path.split(this.pathSeparator);
16940         var curNode = this.root;
16941         if(curNode.attributes[attr] != keys[1]){ // invalid root
16942             if(callback){
16943                 callback(false, null);
16944             }
16945             return;
16946         }
16947         var index = 1;
16948         var f = function(){
16949             if(++index == keys.length){
16950                 if(callback){
16951                     callback(true, curNode);
16952                 }
16953                 return;
16954             }
16955             var c = curNode.findChild(attr, keys[index]);
16956             if(!c){
16957                 if(callback){
16958                     callback(false, curNode);
16959                 }
16960                 return;
16961             }
16962             curNode = c;
16963             c.expand(false, false, f);
16964         };
16965         curNode.expand(false, false, f);
16966     },
16967
16968     /**
16969      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16970      * @param {String} path
16971      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16972      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16973      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16974      */
16975     selectPath : function(path, attr, callback){
16976         attr = attr || "id";
16977         var keys = path.split(this.pathSeparator);
16978         var v = keys.pop();
16979         if(keys.length > 0){
16980             var f = function(success, node){
16981                 if(success && node){
16982                     var n = node.findChild(attr, v);
16983                     if(n){
16984                         n.select();
16985                         if(callback){
16986                             callback(true, n);
16987                         }
16988                     }else if(callback){
16989                         callback(false, n);
16990                     }
16991                 }else{
16992                     if(callback){
16993                         callback(false, n);
16994                     }
16995                 }
16996             };
16997             this.expandPath(keys.join(this.pathSeparator), attr, f);
16998         }else{
16999             this.root.select();
17000             if(callback){
17001                 callback(true, this.root);
17002             }
17003         }
17004     },
17005
17006     getTreeEl : function(){
17007         return this.el;
17008     },
17009
17010     /**
17011      * Trigger rendering of this TreePanel
17012      */
17013     render : function(){
17014         if (this.innerCt) {
17015             return this; // stop it rendering more than once!!
17016         }
17017         
17018         this.innerCt = this.el.createChild({tag:"ul",
17019                cls:"x-tree-root-ct " +
17020                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
17021
17022         if(this.containerScroll){
17023             Roo.dd.ScrollManager.register(this.el);
17024         }
17025         if((this.enableDD || this.enableDrop) && !this.dropZone){
17026            /**
17027             * The dropZone used by this tree if drop is enabled
17028             * @type Roo.tree.TreeDropZone
17029             */
17030              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
17031                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
17032            });
17033         }
17034         if((this.enableDD || this.enableDrag) && !this.dragZone){
17035            /**
17036             * The dragZone used by this tree if drag is enabled
17037             * @type Roo.tree.TreeDragZone
17038             */
17039             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
17040                ddGroup: this.ddGroup || "TreeDD",
17041                scroll: this.ddScroll
17042            });
17043         }
17044         this.getSelectionModel().init(this);
17045         if (!this.root) {
17046             Roo.log("ROOT not set in tree");
17047             return this;
17048         }
17049         this.root.render();
17050         if(!this.rootVisible){
17051             this.root.renderChildren();
17052         }
17053         return this;
17054     }
17055 });/*
17056  * Based on:
17057  * Ext JS Library 1.1.1
17058  * Copyright(c) 2006-2007, Ext JS, LLC.
17059  *
17060  * Originally Released Under LGPL - original licence link has changed is not relivant.
17061  *
17062  * Fork - LGPL
17063  * <script type="text/javascript">
17064  */
17065  
17066
17067 /**
17068  * @class Roo.tree.DefaultSelectionModel
17069  * @extends Roo.util.Observable
17070  * The default single selection for a TreePanel.
17071  * @param {Object} cfg Configuration
17072  */
17073 Roo.tree.DefaultSelectionModel = function(cfg){
17074    this.selNode = null;
17075    
17076    
17077    
17078    this.addEvents({
17079        /**
17080         * @event selectionchange
17081         * Fires when the selected node changes
17082         * @param {DefaultSelectionModel} this
17083         * @param {TreeNode} node the new selection
17084         */
17085        "selectionchange" : true,
17086
17087        /**
17088         * @event beforeselect
17089         * Fires before the selected node changes, return false to cancel the change
17090         * @param {DefaultSelectionModel} this
17091         * @param {TreeNode} node the new selection
17092         * @param {TreeNode} node the old selection
17093         */
17094        "beforeselect" : true
17095    });
17096    
17097     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
17098 };
17099
17100 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
17101     init : function(tree){
17102         this.tree = tree;
17103         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17104         tree.on("click", this.onNodeClick, this);
17105     },
17106     
17107     onNodeClick : function(node, e){
17108         if (e.ctrlKey && this.selNode == node)  {
17109             this.unselect(node);
17110             return;
17111         }
17112         this.select(node);
17113     },
17114     
17115     /**
17116      * Select a node.
17117      * @param {TreeNode} node The node to select
17118      * @return {TreeNode} The selected node
17119      */
17120     select : function(node){
17121         var last = this.selNode;
17122         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17123             if(last){
17124                 last.ui.onSelectedChange(false);
17125             }
17126             this.selNode = node;
17127             node.ui.onSelectedChange(true);
17128             this.fireEvent("selectionchange", this, node, last);
17129         }
17130         return node;
17131     },
17132     
17133     /**
17134      * Deselect a node.
17135      * @param {TreeNode} node The node to unselect
17136      */
17137     unselect : function(node){
17138         if(this.selNode == node){
17139             this.clearSelections();
17140         }    
17141     },
17142     
17143     /**
17144      * Clear all selections
17145      */
17146     clearSelections : function(){
17147         var n = this.selNode;
17148         if(n){
17149             n.ui.onSelectedChange(false);
17150             this.selNode = null;
17151             this.fireEvent("selectionchange", this, null);
17152         }
17153         return n;
17154     },
17155     
17156     /**
17157      * Get the selected node
17158      * @return {TreeNode} The selected node
17159      */
17160     getSelectedNode : function(){
17161         return this.selNode;    
17162     },
17163     
17164     /**
17165      * Returns true if the node is selected
17166      * @param {TreeNode} node The node to check
17167      * @return {Boolean}
17168      */
17169     isSelected : function(node){
17170         return this.selNode == node;  
17171     },
17172
17173     /**
17174      * Selects the node above the selected node in the tree, intelligently walking the nodes
17175      * @return TreeNode The new selection
17176      */
17177     selectPrevious : function(){
17178         var s = this.selNode || this.lastSelNode;
17179         if(!s){
17180             return null;
17181         }
17182         var ps = s.previousSibling;
17183         if(ps){
17184             if(!ps.isExpanded() || ps.childNodes.length < 1){
17185                 return this.select(ps);
17186             } else{
17187                 var lc = ps.lastChild;
17188                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17189                     lc = lc.lastChild;
17190                 }
17191                 return this.select(lc);
17192             }
17193         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17194             return this.select(s.parentNode);
17195         }
17196         return null;
17197     },
17198
17199     /**
17200      * Selects the node above the selected node in the tree, intelligently walking the nodes
17201      * @return TreeNode The new selection
17202      */
17203     selectNext : function(){
17204         var s = this.selNode || this.lastSelNode;
17205         if(!s){
17206             return null;
17207         }
17208         if(s.firstChild && s.isExpanded()){
17209              return this.select(s.firstChild);
17210          }else if(s.nextSibling){
17211              return this.select(s.nextSibling);
17212          }else if(s.parentNode){
17213             var newS = null;
17214             s.parentNode.bubble(function(){
17215                 if(this.nextSibling){
17216                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17217                     return false;
17218                 }
17219             });
17220             return newS;
17221          }
17222         return null;
17223     },
17224
17225     onKeyDown : function(e){
17226         var s = this.selNode || this.lastSelNode;
17227         // undesirable, but required
17228         var sm = this;
17229         if(!s){
17230             return;
17231         }
17232         var k = e.getKey();
17233         switch(k){
17234              case e.DOWN:
17235                  e.stopEvent();
17236                  this.selectNext();
17237              break;
17238              case e.UP:
17239                  e.stopEvent();
17240                  this.selectPrevious();
17241              break;
17242              case e.RIGHT:
17243                  e.preventDefault();
17244                  if(s.hasChildNodes()){
17245                      if(!s.isExpanded()){
17246                          s.expand();
17247                      }else if(s.firstChild){
17248                          this.select(s.firstChild, e);
17249                      }
17250                  }
17251              break;
17252              case e.LEFT:
17253                  e.preventDefault();
17254                  if(s.hasChildNodes() && s.isExpanded()){
17255                      s.collapse();
17256                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17257                      this.select(s.parentNode, e);
17258                  }
17259              break;
17260         };
17261     }
17262 });
17263
17264 /**
17265  * @class Roo.tree.MultiSelectionModel
17266  * @extends Roo.util.Observable
17267  * Multi selection for a TreePanel.
17268  * @param {Object} cfg Configuration
17269  */
17270 Roo.tree.MultiSelectionModel = function(){
17271    this.selNodes = [];
17272    this.selMap = {};
17273    this.addEvents({
17274        /**
17275         * @event selectionchange
17276         * Fires when the selected nodes change
17277         * @param {MultiSelectionModel} this
17278         * @param {Array} nodes Array of the selected nodes
17279         */
17280        "selectionchange" : true
17281    });
17282    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17283    
17284 };
17285
17286 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17287     init : function(tree){
17288         this.tree = tree;
17289         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17290         tree.on("click", this.onNodeClick, this);
17291     },
17292     
17293     onNodeClick : function(node, e){
17294         this.select(node, e, e.ctrlKey);
17295     },
17296     
17297     /**
17298      * Select a node.
17299      * @param {TreeNode} node The node to select
17300      * @param {EventObject} e (optional) An event associated with the selection
17301      * @param {Boolean} keepExisting True to retain existing selections
17302      * @return {TreeNode} The selected node
17303      */
17304     select : function(node, e, keepExisting){
17305         if(keepExisting !== true){
17306             this.clearSelections(true);
17307         }
17308         if(this.isSelected(node)){
17309             this.lastSelNode = node;
17310             return node;
17311         }
17312         this.selNodes.push(node);
17313         this.selMap[node.id] = node;
17314         this.lastSelNode = node;
17315         node.ui.onSelectedChange(true);
17316         this.fireEvent("selectionchange", this, this.selNodes);
17317         return node;
17318     },
17319     
17320     /**
17321      * Deselect a node.
17322      * @param {TreeNode} node The node to unselect
17323      */
17324     unselect : function(node){
17325         if(this.selMap[node.id]){
17326             node.ui.onSelectedChange(false);
17327             var sn = this.selNodes;
17328             var index = -1;
17329             if(sn.indexOf){
17330                 index = sn.indexOf(node);
17331             }else{
17332                 for(var i = 0, len = sn.length; i < len; i++){
17333                     if(sn[i] == node){
17334                         index = i;
17335                         break;
17336                     }
17337                 }
17338             }
17339             if(index != -1){
17340                 this.selNodes.splice(index, 1);
17341             }
17342             delete this.selMap[node.id];
17343             this.fireEvent("selectionchange", this, this.selNodes);
17344         }
17345     },
17346     
17347     /**
17348      * Clear all selections
17349      */
17350     clearSelections : function(suppressEvent){
17351         var sn = this.selNodes;
17352         if(sn.length > 0){
17353             for(var i = 0, len = sn.length; i < len; i++){
17354                 sn[i].ui.onSelectedChange(false);
17355             }
17356             this.selNodes = [];
17357             this.selMap = {};
17358             if(suppressEvent !== true){
17359                 this.fireEvent("selectionchange", this, this.selNodes);
17360             }
17361         }
17362     },
17363     
17364     /**
17365      * Returns true if the node is selected
17366      * @param {TreeNode} node The node to check
17367      * @return {Boolean}
17368      */
17369     isSelected : function(node){
17370         return this.selMap[node.id] ? true : false;  
17371     },
17372     
17373     /**
17374      * Returns an array of the selected nodes
17375      * @return {Array}
17376      */
17377     getSelectedNodes : function(){
17378         return this.selNodes;    
17379     },
17380
17381     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17382
17383     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17384
17385     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17386 });/*
17387  * Based on:
17388  * Ext JS Library 1.1.1
17389  * Copyright(c) 2006-2007, Ext JS, LLC.
17390  *
17391  * Originally Released Under LGPL - original licence link has changed is not relivant.
17392  *
17393  * Fork - LGPL
17394  * <script type="text/javascript">
17395  */
17396  
17397 /**
17398  * @class Roo.tree.TreeNode
17399  * @extends Roo.data.Node
17400  * @cfg {String} text The text for this node
17401  * @cfg {Boolean} expanded true to start the node expanded
17402  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17403  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17404  * @cfg {Boolean} disabled true to start the node disabled
17405  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17406  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17407  * @cfg {String} cls A css class to be added to the node
17408  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17409  * @cfg {String} href URL of the link used for the node (defaults to #)
17410  * @cfg {String} hrefTarget target frame for the link
17411  * @cfg {String} qtip An Ext QuickTip for the node
17412  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17413  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17414  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17415  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17416  * (defaults to undefined with no checkbox rendered)
17417  * @constructor
17418  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17419  */
17420 Roo.tree.TreeNode = function(attributes){
17421     attributes = attributes || {};
17422     if(typeof attributes == "string"){
17423         attributes = {text: attributes};
17424     }
17425     this.childrenRendered = false;
17426     this.rendered = false;
17427     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17428     this.expanded = attributes.expanded === true;
17429     this.isTarget = attributes.isTarget !== false;
17430     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17431     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17432
17433     /**
17434      * Read-only. The text for this node. To change it use setText().
17435      * @type String
17436      */
17437     this.text = attributes.text;
17438     /**
17439      * True if this node is disabled.
17440      * @type Boolean
17441      */
17442     this.disabled = attributes.disabled === true;
17443
17444     this.addEvents({
17445         /**
17446         * @event textchange
17447         * Fires when the text for this node is changed
17448         * @param {Node} this This node
17449         * @param {String} text The new text
17450         * @param {String} oldText The old text
17451         */
17452         "textchange" : true,
17453         /**
17454         * @event beforeexpand
17455         * Fires before this node is expanded, return false to cancel.
17456         * @param {Node} this This node
17457         * @param {Boolean} deep
17458         * @param {Boolean} anim
17459         */
17460         "beforeexpand" : true,
17461         /**
17462         * @event beforecollapse
17463         * Fires before this node is collapsed, return false to cancel.
17464         * @param {Node} this This node
17465         * @param {Boolean} deep
17466         * @param {Boolean} anim
17467         */
17468         "beforecollapse" : true,
17469         /**
17470         * @event expand
17471         * Fires when this node is expanded
17472         * @param {Node} this This node
17473         */
17474         "expand" : true,
17475         /**
17476         * @event disabledchange
17477         * Fires when the disabled status of this node changes
17478         * @param {Node} this This node
17479         * @param {Boolean} disabled
17480         */
17481         "disabledchange" : true,
17482         /**
17483         * @event collapse
17484         * Fires when this node is collapsed
17485         * @param {Node} this This node
17486         */
17487         "collapse" : true,
17488         /**
17489         * @event beforeclick
17490         * Fires before click processing. Return false to cancel the default action.
17491         * @param {Node} this This node
17492         * @param {Roo.EventObject} e The event object
17493         */
17494         "beforeclick":true,
17495         /**
17496         * @event checkchange
17497         * Fires when a node with a checkbox's checked property changes
17498         * @param {Node} this This node
17499         * @param {Boolean} checked
17500         */
17501         "checkchange":true,
17502         /**
17503         * @event click
17504         * Fires when this node is clicked
17505         * @param {Node} this This node
17506         * @param {Roo.EventObject} e The event object
17507         */
17508         "click":true,
17509         /**
17510         * @event dblclick
17511         * Fires when this node is double clicked
17512         * @param {Node} this This node
17513         * @param {Roo.EventObject} e The event object
17514         */
17515         "dblclick":true,
17516         /**
17517         * @event contextmenu
17518         * Fires when this node is right clicked
17519         * @param {Node} this This node
17520         * @param {Roo.EventObject} e The event object
17521         */
17522         "contextmenu":true,
17523         /**
17524         * @event beforechildrenrendered
17525         * Fires right before the child nodes for this node are rendered
17526         * @param {Node} this This node
17527         */
17528         "beforechildrenrendered":true
17529     });
17530
17531     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17532
17533     /**
17534      * Read-only. The UI for this node
17535      * @type TreeNodeUI
17536      */
17537     this.ui = new uiClass(this);
17538     
17539     // finally support items[]
17540     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17541         return;
17542     }
17543     
17544     
17545     Roo.each(this.attributes.items, function(c) {
17546         this.appendChild(Roo.factory(c,Roo.Tree));
17547     }, this);
17548     delete this.attributes.items;
17549     
17550     
17551     
17552 };
17553 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17554     preventHScroll: true,
17555     /**
17556      * Returns true if this node is expanded
17557      * @return {Boolean}
17558      */
17559     isExpanded : function(){
17560         return this.expanded;
17561     },
17562
17563     /**
17564      * Returns the UI object for this node
17565      * @return {TreeNodeUI}
17566      */
17567     getUI : function(){
17568         return this.ui;
17569     },
17570
17571     // private override
17572     setFirstChild : function(node){
17573         var of = this.firstChild;
17574         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17575         if(this.childrenRendered && of && node != of){
17576             of.renderIndent(true, true);
17577         }
17578         if(this.rendered){
17579             this.renderIndent(true, true);
17580         }
17581     },
17582
17583     // private override
17584     setLastChild : function(node){
17585         var ol = this.lastChild;
17586         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17587         if(this.childrenRendered && ol && node != ol){
17588             ol.renderIndent(true, true);
17589         }
17590         if(this.rendered){
17591             this.renderIndent(true, true);
17592         }
17593     },
17594
17595     // these methods are overridden to provide lazy rendering support
17596     // private override
17597     appendChild : function()
17598     {
17599         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17600         if(node && this.childrenRendered){
17601             node.render();
17602         }
17603         this.ui.updateExpandIcon();
17604         return node;
17605     },
17606
17607     // private override
17608     removeChild : function(node){
17609         this.ownerTree.getSelectionModel().unselect(node);
17610         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17611         // if it's been rendered remove dom node
17612         if(this.childrenRendered){
17613             node.ui.remove();
17614         }
17615         if(this.childNodes.length < 1){
17616             this.collapse(false, false);
17617         }else{
17618             this.ui.updateExpandIcon();
17619         }
17620         if(!this.firstChild) {
17621             this.childrenRendered = false;
17622         }
17623         return node;
17624     },
17625
17626     // private override
17627     insertBefore : function(node, refNode){
17628         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17629         if(newNode && refNode && this.childrenRendered){
17630             node.render();
17631         }
17632         this.ui.updateExpandIcon();
17633         return newNode;
17634     },
17635
17636     /**
17637      * Sets the text for this node
17638      * @param {String} text
17639      */
17640     setText : function(text){
17641         var oldText = this.text;
17642         this.text = text;
17643         this.attributes.text = text;
17644         if(this.rendered){ // event without subscribing
17645             this.ui.onTextChange(this, text, oldText);
17646         }
17647         this.fireEvent("textchange", this, text, oldText);
17648     },
17649
17650     /**
17651      * Triggers selection of this node
17652      */
17653     select : function(){
17654         this.getOwnerTree().getSelectionModel().select(this);
17655     },
17656
17657     /**
17658      * Triggers deselection of this node
17659      */
17660     unselect : function(){
17661         this.getOwnerTree().getSelectionModel().unselect(this);
17662     },
17663
17664     /**
17665      * Returns true if this node is selected
17666      * @return {Boolean}
17667      */
17668     isSelected : function(){
17669         return this.getOwnerTree().getSelectionModel().isSelected(this);
17670     },
17671
17672     /**
17673      * Expand this node.
17674      * @param {Boolean} deep (optional) True to expand all children as well
17675      * @param {Boolean} anim (optional) false to cancel the default animation
17676      * @param {Function} callback (optional) A callback to be called when
17677      * expanding this node completes (does not wait for deep expand to complete).
17678      * Called with 1 parameter, this node.
17679      */
17680     expand : function(deep, anim, callback){
17681         if(!this.expanded){
17682             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17683                 return;
17684             }
17685             if(!this.childrenRendered){
17686                 this.renderChildren();
17687             }
17688             this.expanded = true;
17689             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17690                 this.ui.animExpand(function(){
17691                     this.fireEvent("expand", this);
17692                     if(typeof callback == "function"){
17693                         callback(this);
17694                     }
17695                     if(deep === true){
17696                         this.expandChildNodes(true);
17697                     }
17698                 }.createDelegate(this));
17699                 return;
17700             }else{
17701                 this.ui.expand();
17702                 this.fireEvent("expand", this);
17703                 if(typeof callback == "function"){
17704                     callback(this);
17705                 }
17706             }
17707         }else{
17708            if(typeof callback == "function"){
17709                callback(this);
17710            }
17711         }
17712         if(deep === true){
17713             this.expandChildNodes(true);
17714         }
17715     },
17716
17717     isHiddenRoot : function(){
17718         return this.isRoot && !this.getOwnerTree().rootVisible;
17719     },
17720
17721     /**
17722      * Collapse this node.
17723      * @param {Boolean} deep (optional) True to collapse all children as well
17724      * @param {Boolean} anim (optional) false to cancel the default animation
17725      */
17726     collapse : function(deep, anim){
17727         if(this.expanded && !this.isHiddenRoot()){
17728             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17729                 return;
17730             }
17731             this.expanded = false;
17732             if((this.getOwnerTree().animate && anim !== false) || anim){
17733                 this.ui.animCollapse(function(){
17734                     this.fireEvent("collapse", this);
17735                     if(deep === true){
17736                         this.collapseChildNodes(true);
17737                     }
17738                 }.createDelegate(this));
17739                 return;
17740             }else{
17741                 this.ui.collapse();
17742                 this.fireEvent("collapse", this);
17743             }
17744         }
17745         if(deep === true){
17746             var cs = this.childNodes;
17747             for(var i = 0, len = cs.length; i < len; i++) {
17748                 cs[i].collapse(true, false);
17749             }
17750         }
17751     },
17752
17753     // private
17754     delayedExpand : function(delay){
17755         if(!this.expandProcId){
17756             this.expandProcId = this.expand.defer(delay, this);
17757         }
17758     },
17759
17760     // private
17761     cancelExpand : function(){
17762         if(this.expandProcId){
17763             clearTimeout(this.expandProcId);
17764         }
17765         this.expandProcId = false;
17766     },
17767
17768     /**
17769      * Toggles expanded/collapsed state of the node
17770      */
17771     toggle : function(){
17772         if(this.expanded){
17773             this.collapse();
17774         }else{
17775             this.expand();
17776         }
17777     },
17778
17779     /**
17780      * Ensures all parent nodes are expanded
17781      */
17782     ensureVisible : function(callback){
17783         var tree = this.getOwnerTree();
17784         tree.expandPath(this.parentNode.getPath(), false, function(){
17785             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17786             Roo.callback(callback);
17787         }.createDelegate(this));
17788     },
17789
17790     /**
17791      * Expand all child nodes
17792      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17793      */
17794     expandChildNodes : function(deep){
17795         var cs = this.childNodes;
17796         for(var i = 0, len = cs.length; i < len; i++) {
17797                 cs[i].expand(deep);
17798         }
17799     },
17800
17801     /**
17802      * Collapse all child nodes
17803      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17804      */
17805     collapseChildNodes : function(deep){
17806         var cs = this.childNodes;
17807         for(var i = 0, len = cs.length; i < len; i++) {
17808                 cs[i].collapse(deep);
17809         }
17810     },
17811
17812     /**
17813      * Disables this node
17814      */
17815     disable : function(){
17816         this.disabled = true;
17817         this.unselect();
17818         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17819             this.ui.onDisableChange(this, true);
17820         }
17821         this.fireEvent("disabledchange", this, true);
17822     },
17823
17824     /**
17825      * Enables this node
17826      */
17827     enable : function(){
17828         this.disabled = false;
17829         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17830             this.ui.onDisableChange(this, false);
17831         }
17832         this.fireEvent("disabledchange", this, false);
17833     },
17834
17835     // private
17836     renderChildren : function(suppressEvent){
17837         if(suppressEvent !== false){
17838             this.fireEvent("beforechildrenrendered", this);
17839         }
17840         var cs = this.childNodes;
17841         for(var i = 0, len = cs.length; i < len; i++){
17842             cs[i].render(true);
17843         }
17844         this.childrenRendered = true;
17845     },
17846
17847     // private
17848     sort : function(fn, scope){
17849         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17850         if(this.childrenRendered){
17851             var cs = this.childNodes;
17852             for(var i = 0, len = cs.length; i < len; i++){
17853                 cs[i].render(true);
17854             }
17855         }
17856     },
17857
17858     // private
17859     render : function(bulkRender){
17860         this.ui.render(bulkRender);
17861         if(!this.rendered){
17862             this.rendered = true;
17863             if(this.expanded){
17864                 this.expanded = false;
17865                 this.expand(false, false);
17866             }
17867         }
17868     },
17869
17870     // private
17871     renderIndent : function(deep, refresh){
17872         if(refresh){
17873             this.ui.childIndent = null;
17874         }
17875         this.ui.renderIndent();
17876         if(deep === true && this.childrenRendered){
17877             var cs = this.childNodes;
17878             for(var i = 0, len = cs.length; i < len; i++){
17879                 cs[i].renderIndent(true, refresh);
17880             }
17881         }
17882     }
17883 });/*
17884  * Based on:
17885  * Ext JS Library 1.1.1
17886  * Copyright(c) 2006-2007, Ext JS, LLC.
17887  *
17888  * Originally Released Under LGPL - original licence link has changed is not relivant.
17889  *
17890  * Fork - LGPL
17891  * <script type="text/javascript">
17892  */
17893  
17894 /**
17895  * @class Roo.tree.AsyncTreeNode
17896  * @extends Roo.tree.TreeNode
17897  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17898  * @constructor
17899  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17900  */
17901  Roo.tree.AsyncTreeNode = function(config){
17902     this.loaded = false;
17903     this.loading = false;
17904     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17905     /**
17906     * @event beforeload
17907     * Fires before this node is loaded, return false to cancel
17908     * @param {Node} this This node
17909     */
17910     this.addEvents({'beforeload':true, 'load': true});
17911     /**
17912     * @event load
17913     * Fires when this node is loaded
17914     * @param {Node} this This node
17915     */
17916     /**
17917      * The loader used by this node (defaults to using the tree's defined loader)
17918      * @type TreeLoader
17919      * @property loader
17920      */
17921 };
17922 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17923     expand : function(deep, anim, callback){
17924         if(this.loading){ // if an async load is already running, waiting til it's done
17925             var timer;
17926             var f = function(){
17927                 if(!this.loading){ // done loading
17928                     clearInterval(timer);
17929                     this.expand(deep, anim, callback);
17930                 }
17931             }.createDelegate(this);
17932             timer = setInterval(f, 200);
17933             return;
17934         }
17935         if(!this.loaded){
17936             if(this.fireEvent("beforeload", this) === false){
17937                 return;
17938             }
17939             this.loading = true;
17940             this.ui.beforeLoad(this);
17941             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17942             if(loader){
17943                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17944                 return;
17945             }
17946         }
17947         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17948     },
17949     
17950     /**
17951      * Returns true if this node is currently loading
17952      * @return {Boolean}
17953      */
17954     isLoading : function(){
17955         return this.loading;  
17956     },
17957     
17958     loadComplete : function(deep, anim, callback){
17959         this.loading = false;
17960         this.loaded = true;
17961         this.ui.afterLoad(this);
17962         this.fireEvent("load", this);
17963         this.expand(deep, anim, callback);
17964     },
17965     
17966     /**
17967      * Returns true if this node has been loaded
17968      * @return {Boolean}
17969      */
17970     isLoaded : function(){
17971         return this.loaded;
17972     },
17973     
17974     hasChildNodes : function(){
17975         if(!this.isLeaf() && !this.loaded){
17976             return true;
17977         }else{
17978             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17979         }
17980     },
17981
17982     /**
17983      * Trigger a reload for this node
17984      * @param {Function} callback
17985      */
17986     reload : function(callback){
17987         this.collapse(false, false);
17988         while(this.firstChild){
17989             this.removeChild(this.firstChild);
17990         }
17991         this.childrenRendered = false;
17992         this.loaded = false;
17993         if(this.isHiddenRoot()){
17994             this.expanded = false;
17995         }
17996         this.expand(false, false, callback);
17997     }
17998 });/*
17999  * Based on:
18000  * Ext JS Library 1.1.1
18001  * Copyright(c) 2006-2007, Ext JS, LLC.
18002  *
18003  * Originally Released Under LGPL - original licence link has changed is not relivant.
18004  *
18005  * Fork - LGPL
18006  * <script type="text/javascript">
18007  */
18008  
18009 /**
18010  * @class Roo.tree.TreeNodeUI
18011  * @constructor
18012  * @param {Object} node The node to render
18013  * The TreeNode UI implementation is separate from the
18014  * tree implementation. Unless you are customizing the tree UI,
18015  * you should never have to use this directly.
18016  */
18017 Roo.tree.TreeNodeUI = function(node){
18018     this.node = node;
18019     this.rendered = false;
18020     this.animating = false;
18021     this.emptyIcon = Roo.BLANK_IMAGE_URL;
18022 };
18023
18024 Roo.tree.TreeNodeUI.prototype = {
18025     removeChild : function(node){
18026         if(this.rendered){
18027             this.ctNode.removeChild(node.ui.getEl());
18028         }
18029     },
18030
18031     beforeLoad : function(){
18032          this.addClass("x-tree-node-loading");
18033     },
18034
18035     afterLoad : function(){
18036          this.removeClass("x-tree-node-loading");
18037     },
18038
18039     onTextChange : function(node, text, oldText){
18040         if(this.rendered){
18041             this.textNode.innerHTML = text;
18042         }
18043     },
18044
18045     onDisableChange : function(node, state){
18046         this.disabled = state;
18047         if(state){
18048             this.addClass("x-tree-node-disabled");
18049         }else{
18050             this.removeClass("x-tree-node-disabled");
18051         }
18052     },
18053
18054     onSelectedChange : function(state){
18055         if(state){
18056             this.focus();
18057             this.addClass("x-tree-selected");
18058         }else{
18059             //this.blur();
18060             this.removeClass("x-tree-selected");
18061         }
18062     },
18063
18064     onMove : function(tree, node, oldParent, newParent, index, refNode){
18065         this.childIndent = null;
18066         if(this.rendered){
18067             var targetNode = newParent.ui.getContainer();
18068             if(!targetNode){//target not rendered
18069                 this.holder = document.createElement("div");
18070                 this.holder.appendChild(this.wrap);
18071                 return;
18072             }
18073             var insertBefore = refNode ? refNode.ui.getEl() : null;
18074             if(insertBefore){
18075                 targetNode.insertBefore(this.wrap, insertBefore);
18076             }else{
18077                 targetNode.appendChild(this.wrap);
18078             }
18079             this.node.renderIndent(true);
18080         }
18081     },
18082
18083     addClass : function(cls){
18084         if(this.elNode){
18085             Roo.fly(this.elNode).addClass(cls);
18086         }
18087     },
18088
18089     removeClass : function(cls){
18090         if(this.elNode){
18091             Roo.fly(this.elNode).removeClass(cls);
18092         }
18093     },
18094
18095     remove : function(){
18096         if(this.rendered){
18097             this.holder = document.createElement("div");
18098             this.holder.appendChild(this.wrap);
18099         }
18100     },
18101
18102     fireEvent : function(){
18103         return this.node.fireEvent.apply(this.node, arguments);
18104     },
18105
18106     initEvents : function(){
18107         this.node.on("move", this.onMove, this);
18108         var E = Roo.EventManager;
18109         var a = this.anchor;
18110
18111         var el = Roo.fly(a, '_treeui');
18112
18113         if(Roo.isOpera){ // opera render bug ignores the CSS
18114             el.setStyle("text-decoration", "none");
18115         }
18116
18117         el.on("click", this.onClick, this);
18118         el.on("dblclick", this.onDblClick, this);
18119
18120         if(this.checkbox){
18121             Roo.EventManager.on(this.checkbox,
18122                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18123         }
18124
18125         el.on("contextmenu", this.onContextMenu, this);
18126
18127         var icon = Roo.fly(this.iconNode);
18128         icon.on("click", this.onClick, this);
18129         icon.on("dblclick", this.onDblClick, this);
18130         icon.on("contextmenu", this.onContextMenu, this);
18131         E.on(this.ecNode, "click", this.ecClick, this, true);
18132
18133         if(this.node.disabled){
18134             this.addClass("x-tree-node-disabled");
18135         }
18136         if(this.node.hidden){
18137             this.addClass("x-tree-node-disabled");
18138         }
18139         var ot = this.node.getOwnerTree();
18140         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18141         if(dd && (!this.node.isRoot || ot.rootVisible)){
18142             Roo.dd.Registry.register(this.elNode, {
18143                 node: this.node,
18144                 handles: this.getDDHandles(),
18145                 isHandle: false
18146             });
18147         }
18148     },
18149
18150     getDDHandles : function(){
18151         return [this.iconNode, this.textNode];
18152     },
18153
18154     hide : function(){
18155         if(this.rendered){
18156             this.wrap.style.display = "none";
18157         }
18158     },
18159
18160     show : function(){
18161         if(this.rendered){
18162             this.wrap.style.display = "";
18163         }
18164     },
18165
18166     onContextMenu : function(e){
18167         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18168             e.preventDefault();
18169             this.focus();
18170             this.fireEvent("contextmenu", this.node, e);
18171         }
18172     },
18173
18174     onClick : function(e){
18175         if(this.dropping){
18176             e.stopEvent();
18177             return;
18178         }
18179         if(this.fireEvent("beforeclick", this.node, e) !== false){
18180             if(!this.disabled && this.node.attributes.href){
18181                 this.fireEvent("click", this.node, e);
18182                 return;
18183             }
18184             e.preventDefault();
18185             if(this.disabled){
18186                 return;
18187             }
18188
18189             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18190                 this.node.toggle();
18191             }
18192
18193             this.fireEvent("click", this.node, e);
18194         }else{
18195             e.stopEvent();
18196         }
18197     },
18198
18199     onDblClick : function(e){
18200         e.preventDefault();
18201         if(this.disabled){
18202             return;
18203         }
18204         if(this.checkbox){
18205             this.toggleCheck();
18206         }
18207         if(!this.animating && this.node.hasChildNodes()){
18208             this.node.toggle();
18209         }
18210         this.fireEvent("dblclick", this.node, e);
18211     },
18212
18213     onCheckChange : function(){
18214         var checked = this.checkbox.checked;
18215         this.node.attributes.checked = checked;
18216         this.fireEvent('checkchange', this.node, checked);
18217     },
18218
18219     ecClick : function(e){
18220         if(!this.animating && this.node.hasChildNodes()){
18221             this.node.toggle();
18222         }
18223     },
18224
18225     startDrop : function(){
18226         this.dropping = true;
18227     },
18228
18229     // delayed drop so the click event doesn't get fired on a drop
18230     endDrop : function(){
18231        setTimeout(function(){
18232            this.dropping = false;
18233        }.createDelegate(this), 50);
18234     },
18235
18236     expand : function(){
18237         this.updateExpandIcon();
18238         this.ctNode.style.display = "";
18239     },
18240
18241     focus : function(){
18242         if(!this.node.preventHScroll){
18243             try{this.anchor.focus();
18244             }catch(e){}
18245         }else if(!Roo.isIE){
18246             try{
18247                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18248                 var l = noscroll.scrollLeft;
18249                 this.anchor.focus();
18250                 noscroll.scrollLeft = l;
18251             }catch(e){}
18252         }
18253     },
18254
18255     toggleCheck : function(value){
18256         var cb = this.checkbox;
18257         if(cb){
18258             cb.checked = (value === undefined ? !cb.checked : value);
18259         }
18260     },
18261
18262     blur : function(){
18263         try{
18264             this.anchor.blur();
18265         }catch(e){}
18266     },
18267
18268     animExpand : function(callback){
18269         var ct = Roo.get(this.ctNode);
18270         ct.stopFx();
18271         if(!this.node.hasChildNodes()){
18272             this.updateExpandIcon();
18273             this.ctNode.style.display = "";
18274             Roo.callback(callback);
18275             return;
18276         }
18277         this.animating = true;
18278         this.updateExpandIcon();
18279
18280         ct.slideIn('t', {
18281            callback : function(){
18282                this.animating = false;
18283                Roo.callback(callback);
18284             },
18285             scope: this,
18286             duration: this.node.ownerTree.duration || .25
18287         });
18288     },
18289
18290     highlight : function(){
18291         var tree = this.node.getOwnerTree();
18292         Roo.fly(this.wrap).highlight(
18293             tree.hlColor || "C3DAF9",
18294             {endColor: tree.hlBaseColor}
18295         );
18296     },
18297
18298     collapse : function(){
18299         this.updateExpandIcon();
18300         this.ctNode.style.display = "none";
18301     },
18302
18303     animCollapse : function(callback){
18304         var ct = Roo.get(this.ctNode);
18305         ct.enableDisplayMode('block');
18306         ct.stopFx();
18307
18308         this.animating = true;
18309         this.updateExpandIcon();
18310
18311         ct.slideOut('t', {
18312             callback : function(){
18313                this.animating = false;
18314                Roo.callback(callback);
18315             },
18316             scope: this,
18317             duration: this.node.ownerTree.duration || .25
18318         });
18319     },
18320
18321     getContainer : function(){
18322         return this.ctNode;
18323     },
18324
18325     getEl : function(){
18326         return this.wrap;
18327     },
18328
18329     appendDDGhost : function(ghostNode){
18330         ghostNode.appendChild(this.elNode.cloneNode(true));
18331     },
18332
18333     getDDRepairXY : function(){
18334         return Roo.lib.Dom.getXY(this.iconNode);
18335     },
18336
18337     onRender : function(){
18338         this.render();
18339     },
18340
18341     render : function(bulkRender){
18342         var n = this.node, a = n.attributes;
18343         var targetNode = n.parentNode ?
18344               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18345
18346         if(!this.rendered){
18347             this.rendered = true;
18348
18349             this.renderElements(n, a, targetNode, bulkRender);
18350
18351             if(a.qtip){
18352                if(this.textNode.setAttributeNS){
18353                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18354                    if(a.qtipTitle){
18355                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18356                    }
18357                }else{
18358                    this.textNode.setAttribute("ext:qtip", a.qtip);
18359                    if(a.qtipTitle){
18360                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18361                    }
18362                }
18363             }else if(a.qtipCfg){
18364                 a.qtipCfg.target = Roo.id(this.textNode);
18365                 Roo.QuickTips.register(a.qtipCfg);
18366             }
18367             this.initEvents();
18368             if(!this.node.expanded){
18369                 this.updateExpandIcon();
18370             }
18371         }else{
18372             if(bulkRender === true) {
18373                 targetNode.appendChild(this.wrap);
18374             }
18375         }
18376     },
18377
18378     renderElements : function(n, a, targetNode, bulkRender)
18379     {
18380         // add some indent caching, this helps performance when rendering a large tree
18381         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18382         var t = n.getOwnerTree();
18383         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18384         if (typeof(n.attributes.html) != 'undefined') {
18385             txt = n.attributes.html;
18386         }
18387         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18388         var cb = typeof a.checked == 'boolean';
18389         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18390         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18391             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18392             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18393             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18394             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18395             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18396              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18397                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18398             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18399             "</li>"];
18400
18401         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18402             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18403                                 n.nextSibling.ui.getEl(), buf.join(""));
18404         }else{
18405             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18406         }
18407
18408         this.elNode = this.wrap.childNodes[0];
18409         this.ctNode = this.wrap.childNodes[1];
18410         var cs = this.elNode.childNodes;
18411         this.indentNode = cs[0];
18412         this.ecNode = cs[1];
18413         this.iconNode = cs[2];
18414         var index = 3;
18415         if(cb){
18416             this.checkbox = cs[3];
18417             index++;
18418         }
18419         this.anchor = cs[index];
18420         this.textNode = cs[index].firstChild;
18421     },
18422
18423     getAnchor : function(){
18424         return this.anchor;
18425     },
18426
18427     getTextEl : function(){
18428         return this.textNode;
18429     },
18430
18431     getIconEl : function(){
18432         return this.iconNode;
18433     },
18434
18435     isChecked : function(){
18436         return this.checkbox ? this.checkbox.checked : false;
18437     },
18438
18439     updateExpandIcon : function(){
18440         if(this.rendered){
18441             var n = this.node, c1, c2;
18442             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18443             var hasChild = n.hasChildNodes();
18444             if(hasChild){
18445                 if(n.expanded){
18446                     cls += "-minus";
18447                     c1 = "x-tree-node-collapsed";
18448                     c2 = "x-tree-node-expanded";
18449                 }else{
18450                     cls += "-plus";
18451                     c1 = "x-tree-node-expanded";
18452                     c2 = "x-tree-node-collapsed";
18453                 }
18454                 if(this.wasLeaf){
18455                     this.removeClass("x-tree-node-leaf");
18456                     this.wasLeaf = false;
18457                 }
18458                 if(this.c1 != c1 || this.c2 != c2){
18459                     Roo.fly(this.elNode).replaceClass(c1, c2);
18460                     this.c1 = c1; this.c2 = c2;
18461                 }
18462             }else{
18463                 // this changes non-leafs into leafs if they have no children.
18464                 // it's not very rational behaviour..
18465                 
18466                 if(!this.wasLeaf && this.node.leaf){
18467                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18468                     delete this.c1;
18469                     delete this.c2;
18470                     this.wasLeaf = true;
18471                 }
18472             }
18473             var ecc = "x-tree-ec-icon "+cls;
18474             if(this.ecc != ecc){
18475                 this.ecNode.className = ecc;
18476                 this.ecc = ecc;
18477             }
18478         }
18479     },
18480
18481     getChildIndent : function(){
18482         if(!this.childIndent){
18483             var buf = [];
18484             var p = this.node;
18485             while(p){
18486                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18487                     if(!p.isLast()) {
18488                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18489                     } else {
18490                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18491                     }
18492                 }
18493                 p = p.parentNode;
18494             }
18495             this.childIndent = buf.join("");
18496         }
18497         return this.childIndent;
18498     },
18499
18500     renderIndent : function(){
18501         if(this.rendered){
18502             var indent = "";
18503             var p = this.node.parentNode;
18504             if(p){
18505                 indent = p.ui.getChildIndent();
18506             }
18507             if(this.indentMarkup != indent){ // don't rerender if not required
18508                 this.indentNode.innerHTML = indent;
18509                 this.indentMarkup = indent;
18510             }
18511             this.updateExpandIcon();
18512         }
18513     }
18514 };
18515
18516 Roo.tree.RootTreeNodeUI = function(){
18517     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18518 };
18519 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18520     render : function(){
18521         if(!this.rendered){
18522             var targetNode = this.node.ownerTree.innerCt.dom;
18523             this.node.expanded = true;
18524             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18525             this.wrap = this.ctNode = targetNode.firstChild;
18526         }
18527     },
18528     collapse : function(){
18529     },
18530     expand : function(){
18531     }
18532 });/*
18533  * Based on:
18534  * Ext JS Library 1.1.1
18535  * Copyright(c) 2006-2007, Ext JS, LLC.
18536  *
18537  * Originally Released Under LGPL - original licence link has changed is not relivant.
18538  *
18539  * Fork - LGPL
18540  * <script type="text/javascript">
18541  */
18542 /**
18543  * @class Roo.tree.TreeLoader
18544  * @extends Roo.util.Observable
18545  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18546  * nodes from a specified URL. The response must be a javascript Array definition
18547  * who's elements are node definition objects. eg:
18548  * <pre><code>
18549 {  success : true,
18550    data :      [
18551    
18552     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18553     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18554     ]
18555 }
18556
18557
18558 </code></pre>
18559  * <br><br>
18560  * The old style respose with just an array is still supported, but not recommended.
18561  * <br><br>
18562  *
18563  * A server request is sent, and child nodes are loaded only when a node is expanded.
18564  * The loading node's id is passed to the server under the parameter name "node" to
18565  * enable the server to produce the correct child nodes.
18566  * <br><br>
18567  * To pass extra parameters, an event handler may be attached to the "beforeload"
18568  * event, and the parameters specified in the TreeLoader's baseParams property:
18569  * <pre><code>
18570     myTreeLoader.on("beforeload", function(treeLoader, node) {
18571         this.baseParams.category = node.attributes.category;
18572     }, this);
18573 </code></pre><
18574  * This would pass an HTTP parameter called "category" to the server containing
18575  * the value of the Node's "category" attribute.
18576  * @constructor
18577  * Creates a new Treeloader.
18578  * @param {Object} config A config object containing config properties.
18579  */
18580 Roo.tree.TreeLoader = function(config){
18581     this.baseParams = {};
18582     this.requestMethod = "POST";
18583     Roo.apply(this, config);
18584
18585     this.addEvents({
18586     
18587         /**
18588          * @event beforeload
18589          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18590          * @param {Object} This TreeLoader object.
18591          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18592          * @param {Object} callback The callback function specified in the {@link #load} call.
18593          */
18594         beforeload : true,
18595         /**
18596          * @event load
18597          * Fires when the node has been successfuly loaded.
18598          * @param {Object} This TreeLoader object.
18599          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18600          * @param {Object} response The response object containing the data from the server.
18601          */
18602         load : true,
18603         /**
18604          * @event loadexception
18605          * Fires if the network request failed.
18606          * @param {Object} This TreeLoader object.
18607          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18608          * @param {Object} response The response object containing the data from the server.
18609          */
18610         loadexception : true,
18611         /**
18612          * @event create
18613          * Fires before a node is created, enabling you to return custom Node types 
18614          * @param {Object} This TreeLoader object.
18615          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18616          */
18617         create : true
18618     });
18619
18620     Roo.tree.TreeLoader.superclass.constructor.call(this);
18621 };
18622
18623 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18624     /**
18625     * @cfg {String} dataUrl The URL from which to request a Json string which
18626     * specifies an array of node definition object representing the child nodes
18627     * to be loaded.
18628     */
18629     /**
18630     * @cfg {String} requestMethod either GET or POST
18631     * defaults to POST (due to BC)
18632     * to be loaded.
18633     */
18634     /**
18635     * @cfg {Object} baseParams (optional) An object containing properties which
18636     * specify HTTP parameters to be passed to each request for child nodes.
18637     */
18638     /**
18639     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18640     * created by this loader. If the attributes sent by the server have an attribute in this object,
18641     * they take priority.
18642     */
18643     /**
18644     * @cfg {Object} uiProviders (optional) An object containing properties which
18645     * 
18646     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18647     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18648     * <i>uiProvider</i> attribute of a returned child node is a string rather
18649     * than a reference to a TreeNodeUI implementation, this that string value
18650     * is used as a property name in the uiProviders object. You can define the provider named
18651     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18652     */
18653     uiProviders : {},
18654
18655     /**
18656     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18657     * child nodes before loading.
18658     */
18659     clearOnLoad : true,
18660
18661     /**
18662     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18663     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18664     * Grid query { data : [ .....] }
18665     */
18666     
18667     root : false,
18668      /**
18669     * @cfg {String} queryParam (optional) 
18670     * Name of the query as it will be passed on the querystring (defaults to 'node')
18671     * eg. the request will be ?node=[id]
18672     */
18673     
18674     
18675     queryParam: false,
18676     
18677     /**
18678      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18679      * This is called automatically when a node is expanded, but may be used to reload
18680      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18681      * @param {Roo.tree.TreeNode} node
18682      * @param {Function} callback
18683      */
18684     load : function(node, callback){
18685         if(this.clearOnLoad){
18686             while(node.firstChild){
18687                 node.removeChild(node.firstChild);
18688             }
18689         }
18690         if(node.attributes.children){ // preloaded json children
18691             var cs = node.attributes.children;
18692             for(var i = 0, len = cs.length; i < len; i++){
18693                 node.appendChild(this.createNode(cs[i]));
18694             }
18695             if(typeof callback == "function"){
18696                 callback();
18697             }
18698         }else if(this.dataUrl){
18699             this.requestData(node, callback);
18700         }
18701     },
18702
18703     getParams: function(node){
18704         var buf = [], bp = this.baseParams;
18705         for(var key in bp){
18706             if(typeof bp[key] != "function"){
18707                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18708             }
18709         }
18710         var n = this.queryParam === false ? 'node' : this.queryParam;
18711         buf.push(n + "=", encodeURIComponent(node.id));
18712         return buf.join("");
18713     },
18714
18715     requestData : function(node, callback){
18716         if(this.fireEvent("beforeload", this, node, callback) !== false){
18717             this.transId = Roo.Ajax.request({
18718                 method:this.requestMethod,
18719                 url: this.dataUrl||this.url,
18720                 success: this.handleResponse,
18721                 failure: this.handleFailure,
18722                 scope: this,
18723                 argument: {callback: callback, node: node},
18724                 params: this.getParams(node)
18725             });
18726         }else{
18727             // if the load is cancelled, make sure we notify
18728             // the node that we are done
18729             if(typeof callback == "function"){
18730                 callback();
18731             }
18732         }
18733     },
18734
18735     isLoading : function(){
18736         return this.transId ? true : false;
18737     },
18738
18739     abort : function(){
18740         if(this.isLoading()){
18741             Roo.Ajax.abort(this.transId);
18742         }
18743     },
18744
18745     // private
18746     createNode : function(attr)
18747     {
18748         // apply baseAttrs, nice idea Corey!
18749         if(this.baseAttrs){
18750             Roo.applyIf(attr, this.baseAttrs);
18751         }
18752         if(this.applyLoader !== false){
18753             attr.loader = this;
18754         }
18755         // uiProvider = depreciated..
18756         
18757         if(typeof(attr.uiProvider) == 'string'){
18758            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18759                 /**  eval:var:attr */ eval(attr.uiProvider);
18760         }
18761         if(typeof(this.uiProviders['default']) != 'undefined') {
18762             attr.uiProvider = this.uiProviders['default'];
18763         }
18764         
18765         this.fireEvent('create', this, attr);
18766         
18767         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18768         return(attr.leaf ?
18769                         new Roo.tree.TreeNode(attr) :
18770                         new Roo.tree.AsyncTreeNode(attr));
18771     },
18772
18773     processResponse : function(response, node, callback)
18774     {
18775         var json = response.responseText;
18776         try {
18777             
18778             var o = Roo.decode(json);
18779             
18780             if (this.root === false && typeof(o.success) != undefined) {
18781                 this.root = 'data'; // the default behaviour for list like data..
18782                 }
18783                 
18784             if (this.root !== false &&  !o.success) {
18785                 // it's a failure condition.
18786                 var a = response.argument;
18787                 this.fireEvent("loadexception", this, a.node, response);
18788                 Roo.log("Load failed - should have a handler really");
18789                 return;
18790             }
18791             
18792             
18793             
18794             if (this.root !== false) {
18795                  o = o[this.root];
18796             }
18797             
18798             for(var i = 0, len = o.length; i < len; i++){
18799                 var n = this.createNode(o[i]);
18800                 if(n){
18801                     node.appendChild(n);
18802                 }
18803             }
18804             if(typeof callback == "function"){
18805                 callback(this, node);
18806             }
18807         }catch(e){
18808             this.handleFailure(response);
18809         }
18810     },
18811
18812     handleResponse : function(response){
18813         this.transId = false;
18814         var a = response.argument;
18815         this.processResponse(response, a.node, a.callback);
18816         this.fireEvent("load", this, a.node, response);
18817     },
18818
18819     handleFailure : function(response)
18820     {
18821         // should handle failure better..
18822         this.transId = false;
18823         var a = response.argument;
18824         this.fireEvent("loadexception", this, a.node, response);
18825         if(typeof a.callback == "function"){
18826             a.callback(this, a.node);
18827         }
18828     }
18829 });/*
18830  * Based on:
18831  * Ext JS Library 1.1.1
18832  * Copyright(c) 2006-2007, Ext JS, LLC.
18833  *
18834  * Originally Released Under LGPL - original licence link has changed is not relivant.
18835  *
18836  * Fork - LGPL
18837  * <script type="text/javascript">
18838  */
18839
18840 /**
18841 * @class Roo.tree.TreeFilter
18842 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18843 * @param {TreePanel} tree
18844 * @param {Object} config (optional)
18845  */
18846 Roo.tree.TreeFilter = function(tree, config){
18847     this.tree = tree;
18848     this.filtered = {};
18849     Roo.apply(this, config);
18850 };
18851
18852 Roo.tree.TreeFilter.prototype = {
18853     clearBlank:false,
18854     reverse:false,
18855     autoClear:false,
18856     remove:false,
18857
18858      /**
18859      * Filter the data by a specific attribute.
18860      * @param {String/RegExp} value Either string that the attribute value
18861      * should start with or a RegExp to test against the attribute
18862      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18863      * @param {TreeNode} startNode (optional) The node to start the filter at.
18864      */
18865     filter : function(value, attr, startNode){
18866         attr = attr || "text";
18867         var f;
18868         if(typeof value == "string"){
18869             var vlen = value.length;
18870             // auto clear empty filter
18871             if(vlen == 0 && this.clearBlank){
18872                 this.clear();
18873                 return;
18874             }
18875             value = value.toLowerCase();
18876             f = function(n){
18877                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18878             };
18879         }else if(value.exec){ // regex?
18880             f = function(n){
18881                 return value.test(n.attributes[attr]);
18882             };
18883         }else{
18884             throw 'Illegal filter type, must be string or regex';
18885         }
18886         this.filterBy(f, null, startNode);
18887         },
18888
18889     /**
18890      * Filter by a function. The passed function will be called with each
18891      * node in the tree (or from the startNode). If the function returns true, the node is kept
18892      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18893      * @param {Function} fn The filter function
18894      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18895      */
18896     filterBy : function(fn, scope, startNode){
18897         startNode = startNode || this.tree.root;
18898         if(this.autoClear){
18899             this.clear();
18900         }
18901         var af = this.filtered, rv = this.reverse;
18902         var f = function(n){
18903             if(n == startNode){
18904                 return true;
18905             }
18906             if(af[n.id]){
18907                 return false;
18908             }
18909             var m = fn.call(scope || n, n);
18910             if(!m || rv){
18911                 af[n.id] = n;
18912                 n.ui.hide();
18913                 return false;
18914             }
18915             return true;
18916         };
18917         startNode.cascade(f);
18918         if(this.remove){
18919            for(var id in af){
18920                if(typeof id != "function"){
18921                    var n = af[id];
18922                    if(n && n.parentNode){
18923                        n.parentNode.removeChild(n);
18924                    }
18925                }
18926            }
18927         }
18928     },
18929
18930     /**
18931      * Clears the current filter. Note: with the "remove" option
18932      * set a filter cannot be cleared.
18933      */
18934     clear : function(){
18935         var t = this.tree;
18936         var af = this.filtered;
18937         for(var id in af){
18938             if(typeof id != "function"){
18939                 var n = af[id];
18940                 if(n){
18941                     n.ui.show();
18942                 }
18943             }
18944         }
18945         this.filtered = {};
18946     }
18947 };
18948 /*
18949  * Based on:
18950  * Ext JS Library 1.1.1
18951  * Copyright(c) 2006-2007, Ext JS, LLC.
18952  *
18953  * Originally Released Under LGPL - original licence link has changed is not relivant.
18954  *
18955  * Fork - LGPL
18956  * <script type="text/javascript">
18957  */
18958  
18959
18960 /**
18961  * @class Roo.tree.TreeSorter
18962  * Provides sorting of nodes in a TreePanel
18963  * 
18964  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18965  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18966  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18967  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18968  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18969  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18970  * @constructor
18971  * @param {TreePanel} tree
18972  * @param {Object} config
18973  */
18974 Roo.tree.TreeSorter = function(tree, config){
18975     Roo.apply(this, config);
18976     tree.on("beforechildrenrendered", this.doSort, this);
18977     tree.on("append", this.updateSort, this);
18978     tree.on("insert", this.updateSort, this);
18979     
18980     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18981     var p = this.property || "text";
18982     var sortType = this.sortType;
18983     var fs = this.folderSort;
18984     var cs = this.caseSensitive === true;
18985     var leafAttr = this.leafAttr || 'leaf';
18986
18987     this.sortFn = function(n1, n2){
18988         if(fs){
18989             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18990                 return 1;
18991             }
18992             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18993                 return -1;
18994             }
18995         }
18996         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18997         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18998         if(v1 < v2){
18999                         return dsc ? +1 : -1;
19000                 }else if(v1 > v2){
19001                         return dsc ? -1 : +1;
19002         }else{
19003                 return 0;
19004         }
19005     };
19006 };
19007
19008 Roo.tree.TreeSorter.prototype = {
19009     doSort : function(node){
19010         node.sort(this.sortFn);
19011     },
19012     
19013     compareNodes : function(n1, n2){
19014         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
19015     },
19016     
19017     updateSort : function(tree, node){
19018         if(node.childrenRendered){
19019             this.doSort.defer(1, this, [node]);
19020         }
19021     }
19022 };/*
19023  * Based on:
19024  * Ext JS Library 1.1.1
19025  * Copyright(c) 2006-2007, Ext JS, LLC.
19026  *
19027  * Originally Released Under LGPL - original licence link has changed is not relivant.
19028  *
19029  * Fork - LGPL
19030  * <script type="text/javascript">
19031  */
19032
19033 if(Roo.dd.DropZone){
19034     
19035 Roo.tree.TreeDropZone = function(tree, config){
19036     this.allowParentInsert = false;
19037     this.allowContainerDrop = false;
19038     this.appendOnly = false;
19039     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
19040     this.tree = tree;
19041     this.lastInsertClass = "x-tree-no-status";
19042     this.dragOverData = {};
19043 };
19044
19045 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
19046     ddGroup : "TreeDD",
19047     scroll:  true,
19048     
19049     expandDelay : 1000,
19050     
19051     expandNode : function(node){
19052         if(node.hasChildNodes() && !node.isExpanded()){
19053             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
19054         }
19055     },
19056     
19057     queueExpand : function(node){
19058         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
19059     },
19060     
19061     cancelExpand : function(){
19062         if(this.expandProcId){
19063             clearTimeout(this.expandProcId);
19064             this.expandProcId = false;
19065         }
19066     },
19067     
19068     isValidDropPoint : function(n, pt, dd, e, data){
19069         if(!n || !data){ return false; }
19070         var targetNode = n.node;
19071         var dropNode = data.node;
19072         // default drop rules
19073         if(!(targetNode && targetNode.isTarget && pt)){
19074             return false;
19075         }
19076         if(pt == "append" && targetNode.allowChildren === false){
19077             return false;
19078         }
19079         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
19080             return false;
19081         }
19082         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
19083             return false;
19084         }
19085         // reuse the object
19086         var overEvent = this.dragOverData;
19087         overEvent.tree = this.tree;
19088         overEvent.target = targetNode;
19089         overEvent.data = data;
19090         overEvent.point = pt;
19091         overEvent.source = dd;
19092         overEvent.rawEvent = e;
19093         overEvent.dropNode = dropNode;
19094         overEvent.cancel = false;  
19095         var result = this.tree.fireEvent("nodedragover", overEvent);
19096         return overEvent.cancel === false && result !== false;
19097     },
19098     
19099     getDropPoint : function(e, n, dd)
19100     {
19101         var tn = n.node;
19102         if(tn.isRoot){
19103             return tn.allowChildren !== false ? "append" : false; // always append for root
19104         }
19105         var dragEl = n.ddel;
19106         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19107         var y = Roo.lib.Event.getPageY(e);
19108         //var noAppend = tn.allowChildren === false || tn.isLeaf();
19109         
19110         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19111         var noAppend = tn.allowChildren === false;
19112         if(this.appendOnly || tn.parentNode.allowChildren === false){
19113             return noAppend ? false : "append";
19114         }
19115         var noBelow = false;
19116         if(!this.allowParentInsert){
19117             noBelow = tn.hasChildNodes() && tn.isExpanded();
19118         }
19119         var q = (b - t) / (noAppend ? 2 : 3);
19120         if(y >= t && y < (t + q)){
19121             return "above";
19122         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19123             return "below";
19124         }else{
19125             return "append";
19126         }
19127     },
19128     
19129     onNodeEnter : function(n, dd, e, data)
19130     {
19131         this.cancelExpand();
19132     },
19133     
19134     onNodeOver : function(n, dd, e, data)
19135     {
19136        
19137         var pt = this.getDropPoint(e, n, dd);
19138         var node = n.node;
19139         
19140         // auto node expand check
19141         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19142             this.queueExpand(node);
19143         }else if(pt != "append"){
19144             this.cancelExpand();
19145         }
19146         
19147         // set the insert point style on the target node
19148         var returnCls = this.dropNotAllowed;
19149         if(this.isValidDropPoint(n, pt, dd, e, data)){
19150            if(pt){
19151                var el = n.ddel;
19152                var cls;
19153                if(pt == "above"){
19154                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19155                    cls = "x-tree-drag-insert-above";
19156                }else if(pt == "below"){
19157                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19158                    cls = "x-tree-drag-insert-below";
19159                }else{
19160                    returnCls = "x-tree-drop-ok-append";
19161                    cls = "x-tree-drag-append";
19162                }
19163                if(this.lastInsertClass != cls){
19164                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19165                    this.lastInsertClass = cls;
19166                }
19167            }
19168        }
19169        return returnCls;
19170     },
19171     
19172     onNodeOut : function(n, dd, e, data){
19173         
19174         this.cancelExpand();
19175         this.removeDropIndicators(n);
19176     },
19177     
19178     onNodeDrop : function(n, dd, e, data){
19179         var point = this.getDropPoint(e, n, dd);
19180         var targetNode = n.node;
19181         targetNode.ui.startDrop();
19182         if(!this.isValidDropPoint(n, point, dd, e, data)){
19183             targetNode.ui.endDrop();
19184             return false;
19185         }
19186         // first try to find the drop node
19187         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19188         var dropEvent = {
19189             tree : this.tree,
19190             target: targetNode,
19191             data: data,
19192             point: point,
19193             source: dd,
19194             rawEvent: e,
19195             dropNode: dropNode,
19196             cancel: !dropNode   
19197         };
19198         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19199         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19200             targetNode.ui.endDrop();
19201             return false;
19202         }
19203         // allow target changing
19204         targetNode = dropEvent.target;
19205         if(point == "append" && !targetNode.isExpanded()){
19206             targetNode.expand(false, null, function(){
19207                 this.completeDrop(dropEvent);
19208             }.createDelegate(this));
19209         }else{
19210             this.completeDrop(dropEvent);
19211         }
19212         return true;
19213     },
19214     
19215     completeDrop : function(de){
19216         var ns = de.dropNode, p = de.point, t = de.target;
19217         if(!(ns instanceof Array)){
19218             ns = [ns];
19219         }
19220         var n;
19221         for(var i = 0, len = ns.length; i < len; i++){
19222             n = ns[i];
19223             if(p == "above"){
19224                 t.parentNode.insertBefore(n, t);
19225             }else if(p == "below"){
19226                 t.parentNode.insertBefore(n, t.nextSibling);
19227             }else{
19228                 t.appendChild(n);
19229             }
19230         }
19231         n.ui.focus();
19232         if(this.tree.hlDrop){
19233             n.ui.highlight();
19234         }
19235         t.ui.endDrop();
19236         this.tree.fireEvent("nodedrop", de);
19237     },
19238     
19239     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19240         if(this.tree.hlDrop){
19241             dropNode.ui.focus();
19242             dropNode.ui.highlight();
19243         }
19244         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19245     },
19246     
19247     getTree : function(){
19248         return this.tree;
19249     },
19250     
19251     removeDropIndicators : function(n){
19252         if(n && n.ddel){
19253             var el = n.ddel;
19254             Roo.fly(el).removeClass([
19255                     "x-tree-drag-insert-above",
19256                     "x-tree-drag-insert-below",
19257                     "x-tree-drag-append"]);
19258             this.lastInsertClass = "_noclass";
19259         }
19260     },
19261     
19262     beforeDragDrop : function(target, e, id){
19263         this.cancelExpand();
19264         return true;
19265     },
19266     
19267     afterRepair : function(data){
19268         if(data && Roo.enableFx){
19269             data.node.ui.highlight();
19270         }
19271         this.hideProxy();
19272     } 
19273     
19274 });
19275
19276 }
19277 /*
19278  * Based on:
19279  * Ext JS Library 1.1.1
19280  * Copyright(c) 2006-2007, Ext JS, LLC.
19281  *
19282  * Originally Released Under LGPL - original licence link has changed is not relivant.
19283  *
19284  * Fork - LGPL
19285  * <script type="text/javascript">
19286  */
19287  
19288
19289 if(Roo.dd.DragZone){
19290 Roo.tree.TreeDragZone = function(tree, config){
19291     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19292     this.tree = tree;
19293 };
19294
19295 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19296     ddGroup : "TreeDD",
19297    
19298     onBeforeDrag : function(data, e){
19299         var n = data.node;
19300         return n && n.draggable && !n.disabled;
19301     },
19302      
19303     
19304     onInitDrag : function(e){
19305         var data = this.dragData;
19306         this.tree.getSelectionModel().select(data.node);
19307         this.proxy.update("");
19308         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19309         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19310     },
19311     
19312     getRepairXY : function(e, data){
19313         return data.node.ui.getDDRepairXY();
19314     },
19315     
19316     onEndDrag : function(data, e){
19317         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19318         
19319         
19320     },
19321     
19322     onValidDrop : function(dd, e, id){
19323         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19324         this.hideProxy();
19325     },
19326     
19327     beforeInvalidDrop : function(e, id){
19328         // this scrolls the original position back into view
19329         var sm = this.tree.getSelectionModel();
19330         sm.clearSelections();
19331         sm.select(this.dragData.node);
19332     }
19333 });
19334 }/*
19335  * Based on:
19336  * Ext JS Library 1.1.1
19337  * Copyright(c) 2006-2007, Ext JS, LLC.
19338  *
19339  * Originally Released Under LGPL - original licence link has changed is not relivant.
19340  *
19341  * Fork - LGPL
19342  * <script type="text/javascript">
19343  */
19344 /**
19345  * @class Roo.tree.TreeEditor
19346  * @extends Roo.Editor
19347  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19348  * as the editor field.
19349  * @constructor
19350  * @param {Object} config (used to be the tree panel.)
19351  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19352  * 
19353  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19354  * @cfg {Roo.form.TextField|Object} field The field configuration
19355  *
19356  * 
19357  */
19358 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19359     var tree = config;
19360     var field;
19361     if (oldconfig) { // old style..
19362         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19363     } else {
19364         // new style..
19365         tree = config.tree;
19366         config.field = config.field  || {};
19367         config.field.xtype = 'TextField';
19368         field = Roo.factory(config.field, Roo.form);
19369     }
19370     config = config || {};
19371     
19372     
19373     this.addEvents({
19374         /**
19375          * @event beforenodeedit
19376          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19377          * false from the handler of this event.
19378          * @param {Editor} this
19379          * @param {Roo.tree.Node} node 
19380          */
19381         "beforenodeedit" : true
19382     });
19383     
19384     //Roo.log(config);
19385     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19386
19387     this.tree = tree;
19388
19389     tree.on('beforeclick', this.beforeNodeClick, this);
19390     tree.getTreeEl().on('mousedown', this.hide, this);
19391     this.on('complete', this.updateNode, this);
19392     this.on('beforestartedit', this.fitToTree, this);
19393     this.on('startedit', this.bindScroll, this, {delay:10});
19394     this.on('specialkey', this.onSpecialKey, this);
19395 };
19396
19397 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19398     /**
19399      * @cfg {String} alignment
19400      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19401      */
19402     alignment: "l-l",
19403     // inherit
19404     autoSize: false,
19405     /**
19406      * @cfg {Boolean} hideEl
19407      * True to hide the bound element while the editor is displayed (defaults to false)
19408      */
19409     hideEl : false,
19410     /**
19411      * @cfg {String} cls
19412      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19413      */
19414     cls: "x-small-editor x-tree-editor",
19415     /**
19416      * @cfg {Boolean} shim
19417      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19418      */
19419     shim:false,
19420     // inherit
19421     shadow:"frame",
19422     /**
19423      * @cfg {Number} maxWidth
19424      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19425      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19426      * scroll and client offsets into account prior to each edit.
19427      */
19428     maxWidth: 250,
19429
19430     editDelay : 350,
19431
19432     // private
19433     fitToTree : function(ed, el){
19434         var td = this.tree.getTreeEl().dom, nd = el.dom;
19435         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19436             td.scrollLeft = nd.offsetLeft;
19437         }
19438         var w = Math.min(
19439                 this.maxWidth,
19440                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19441         this.setSize(w, '');
19442         
19443         return this.fireEvent('beforenodeedit', this, this.editNode);
19444         
19445     },
19446
19447     // private
19448     triggerEdit : function(node){
19449         this.completeEdit();
19450         this.editNode = node;
19451         this.startEdit(node.ui.textNode, node.text);
19452     },
19453
19454     // private
19455     bindScroll : function(){
19456         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19457     },
19458
19459     // private
19460     beforeNodeClick : function(node, e){
19461         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19462         this.lastClick = new Date();
19463         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19464             e.stopEvent();
19465             this.triggerEdit(node);
19466             return false;
19467         }
19468         return true;
19469     },
19470
19471     // private
19472     updateNode : function(ed, value){
19473         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19474         this.editNode.setText(value);
19475     },
19476
19477     // private
19478     onHide : function(){
19479         Roo.tree.TreeEditor.superclass.onHide.call(this);
19480         if(this.editNode){
19481             this.editNode.ui.focus();
19482         }
19483     },
19484
19485     // private
19486     onSpecialKey : function(field, e){
19487         var k = e.getKey();
19488         if(k == e.ESC){
19489             e.stopEvent();
19490             this.cancelEdit();
19491         }else if(k == e.ENTER && !e.hasModifier()){
19492             e.stopEvent();
19493             this.completeEdit();
19494         }
19495     }
19496 });//<Script type="text/javascript">
19497 /*
19498  * Based on:
19499  * Ext JS Library 1.1.1
19500  * Copyright(c) 2006-2007, Ext JS, LLC.
19501  *
19502  * Originally Released Under LGPL - original licence link has changed is not relivant.
19503  *
19504  * Fork - LGPL
19505  * <script type="text/javascript">
19506  */
19507  
19508 /**
19509  * Not documented??? - probably should be...
19510  */
19511
19512 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19513     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19514     
19515     renderElements : function(n, a, targetNode, bulkRender){
19516         //consel.log("renderElements?");
19517         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19518
19519         var t = n.getOwnerTree();
19520         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19521         
19522         var cols = t.columns;
19523         var bw = t.borderWidth;
19524         var c = cols[0];
19525         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19526          var cb = typeof a.checked == "boolean";
19527         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19528         var colcls = 'x-t-' + tid + '-c0';
19529         var buf = [
19530             '<li class="x-tree-node">',
19531             
19532                 
19533                 '<div class="x-tree-node-el ', a.cls,'">',
19534                     // extran...
19535                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19536                 
19537                 
19538                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19539                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19540                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19541                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19542                            (a.iconCls ? ' '+a.iconCls : ''),
19543                            '" unselectable="on" />',
19544                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19545                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19546                              
19547                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19548                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19549                             '<span unselectable="on" qtip="' + tx + '">',
19550                              tx,
19551                              '</span></a>' ,
19552                     '</div>',
19553                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19554                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19555                  ];
19556         for(var i = 1, len = cols.length; i < len; i++){
19557             c = cols[i];
19558             colcls = 'x-t-' + tid + '-c' +i;
19559             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19560             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19561                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19562                       "</div>");
19563          }
19564          
19565          buf.push(
19566             '</a>',
19567             '<div class="x-clear"></div></div>',
19568             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19569             "</li>");
19570         
19571         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19572             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19573                                 n.nextSibling.ui.getEl(), buf.join(""));
19574         }else{
19575             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19576         }
19577         var el = this.wrap.firstChild;
19578         this.elRow = el;
19579         this.elNode = el.firstChild;
19580         this.ranchor = el.childNodes[1];
19581         this.ctNode = this.wrap.childNodes[1];
19582         var cs = el.firstChild.childNodes;
19583         this.indentNode = cs[0];
19584         this.ecNode = cs[1];
19585         this.iconNode = cs[2];
19586         var index = 3;
19587         if(cb){
19588             this.checkbox = cs[3];
19589             index++;
19590         }
19591         this.anchor = cs[index];
19592         
19593         this.textNode = cs[index].firstChild;
19594         
19595         //el.on("click", this.onClick, this);
19596         //el.on("dblclick", this.onDblClick, this);
19597         
19598         
19599        // console.log(this);
19600     },
19601     initEvents : function(){
19602         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19603         
19604             
19605         var a = this.ranchor;
19606
19607         var el = Roo.get(a);
19608
19609         if(Roo.isOpera){ // opera render bug ignores the CSS
19610             el.setStyle("text-decoration", "none");
19611         }
19612
19613         el.on("click", this.onClick, this);
19614         el.on("dblclick", this.onDblClick, this);
19615         el.on("contextmenu", this.onContextMenu, this);
19616         
19617     },
19618     
19619     /*onSelectedChange : function(state){
19620         if(state){
19621             this.focus();
19622             this.addClass("x-tree-selected");
19623         }else{
19624             //this.blur();
19625             this.removeClass("x-tree-selected");
19626         }
19627     },*/
19628     addClass : function(cls){
19629         if(this.elRow){
19630             Roo.fly(this.elRow).addClass(cls);
19631         }
19632         
19633     },
19634     
19635     
19636     removeClass : function(cls){
19637         if(this.elRow){
19638             Roo.fly(this.elRow).removeClass(cls);
19639         }
19640     }
19641
19642     
19643     
19644 });//<Script type="text/javascript">
19645
19646 /*
19647  * Based on:
19648  * Ext JS Library 1.1.1
19649  * Copyright(c) 2006-2007, Ext JS, LLC.
19650  *
19651  * Originally Released Under LGPL - original licence link has changed is not relivant.
19652  *
19653  * Fork - LGPL
19654  * <script type="text/javascript">
19655  */
19656  
19657
19658 /**
19659  * @class Roo.tree.ColumnTree
19660  * @extends Roo.data.TreePanel
19661  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19662  * @cfg {int} borderWidth  compined right/left border allowance
19663  * @constructor
19664  * @param {String/HTMLElement/Element} el The container element
19665  * @param {Object} config
19666  */
19667 Roo.tree.ColumnTree =  function(el, config)
19668 {
19669    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19670    this.addEvents({
19671         /**
19672         * @event resize
19673         * Fire this event on a container when it resizes
19674         * @param {int} w Width
19675         * @param {int} h Height
19676         */
19677        "resize" : true
19678     });
19679     this.on('resize', this.onResize, this);
19680 };
19681
19682 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19683     //lines:false,
19684     
19685     
19686     borderWidth: Roo.isBorderBox ? 0 : 2, 
19687     headEls : false,
19688     
19689     render : function(){
19690         // add the header.....
19691        
19692         Roo.tree.ColumnTree.superclass.render.apply(this);
19693         
19694         this.el.addClass('x-column-tree');
19695         
19696         this.headers = this.el.createChild(
19697             {cls:'x-tree-headers'},this.innerCt.dom);
19698    
19699         var cols = this.columns, c;
19700         var totalWidth = 0;
19701         this.headEls = [];
19702         var  len = cols.length;
19703         for(var i = 0; i < len; i++){
19704              c = cols[i];
19705              totalWidth += c.width;
19706             this.headEls.push(this.headers.createChild({
19707                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19708                  cn: {
19709                      cls:'x-tree-hd-text',
19710                      html: c.header
19711                  },
19712                  style:'width:'+(c.width-this.borderWidth)+'px;'
19713              }));
19714         }
19715         this.headers.createChild({cls:'x-clear'});
19716         // prevent floats from wrapping when clipped
19717         this.headers.setWidth(totalWidth);
19718         //this.innerCt.setWidth(totalWidth);
19719         this.innerCt.setStyle({ overflow: 'auto' });
19720         this.onResize(this.width, this.height);
19721              
19722         
19723     },
19724     onResize : function(w,h)
19725     {
19726         this.height = h;
19727         this.width = w;
19728         // resize cols..
19729         this.innerCt.setWidth(this.width);
19730         this.innerCt.setHeight(this.height-20);
19731         
19732         // headers...
19733         var cols = this.columns, c;
19734         var totalWidth = 0;
19735         var expEl = false;
19736         var len = cols.length;
19737         for(var i = 0; i < len; i++){
19738             c = cols[i];
19739             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19740                 // it's the expander..
19741                 expEl  = this.headEls[i];
19742                 continue;
19743             }
19744             totalWidth += c.width;
19745             
19746         }
19747         if (expEl) {
19748             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19749         }
19750         this.headers.setWidth(w-20);
19751
19752         
19753         
19754         
19755     }
19756 });
19757 /*
19758  * Based on:
19759  * Ext JS Library 1.1.1
19760  * Copyright(c) 2006-2007, Ext JS, LLC.
19761  *
19762  * Originally Released Under LGPL - original licence link has changed is not relivant.
19763  *
19764  * Fork - LGPL
19765  * <script type="text/javascript">
19766  */
19767  
19768 /**
19769  * @class Roo.menu.Menu
19770  * @extends Roo.util.Observable
19771  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19772  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19773  * @constructor
19774  * Creates a new Menu
19775  * @param {Object} config Configuration options
19776  */
19777 Roo.menu.Menu = function(config){
19778     Roo.apply(this, config);
19779     this.id = this.id || Roo.id();
19780     this.addEvents({
19781         /**
19782          * @event beforeshow
19783          * Fires before this menu is displayed
19784          * @param {Roo.menu.Menu} this
19785          */
19786         beforeshow : true,
19787         /**
19788          * @event beforehide
19789          * Fires before this menu is hidden
19790          * @param {Roo.menu.Menu} this
19791          */
19792         beforehide : true,
19793         /**
19794          * @event show
19795          * Fires after this menu is displayed
19796          * @param {Roo.menu.Menu} this
19797          */
19798         show : true,
19799         /**
19800          * @event hide
19801          * Fires after this menu is hidden
19802          * @param {Roo.menu.Menu} this
19803          */
19804         hide : true,
19805         /**
19806          * @event click
19807          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19808          * @param {Roo.menu.Menu} this
19809          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19810          * @param {Roo.EventObject} e
19811          */
19812         click : true,
19813         /**
19814          * @event mouseover
19815          * Fires when the mouse is hovering over this menu
19816          * @param {Roo.menu.Menu} this
19817          * @param {Roo.EventObject} e
19818          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19819          */
19820         mouseover : true,
19821         /**
19822          * @event mouseout
19823          * Fires when the mouse exits this menu
19824          * @param {Roo.menu.Menu} this
19825          * @param {Roo.EventObject} e
19826          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19827          */
19828         mouseout : true,
19829         /**
19830          * @event itemclick
19831          * Fires when a menu item contained in this menu is clicked
19832          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19833          * @param {Roo.EventObject} e
19834          */
19835         itemclick: true
19836     });
19837     if (this.registerMenu) {
19838         Roo.menu.MenuMgr.register(this);
19839     }
19840     
19841     var mis = this.items;
19842     this.items = new Roo.util.MixedCollection();
19843     if(mis){
19844         this.add.apply(this, mis);
19845     }
19846 };
19847
19848 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19849     /**
19850      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19851      */
19852     minWidth : 120,
19853     /**
19854      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19855      * for bottom-right shadow (defaults to "sides")
19856      */
19857     shadow : "sides",
19858     /**
19859      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19860      * this menu (defaults to "tl-tr?")
19861      */
19862     subMenuAlign : "tl-tr?",
19863     /**
19864      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19865      * relative to its element of origin (defaults to "tl-bl?")
19866      */
19867     defaultAlign : "tl-bl?",
19868     /**
19869      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19870      */
19871     allowOtherMenus : false,
19872     /**
19873      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19874      */
19875     registerMenu : true,
19876
19877     hidden:true,
19878
19879     // private
19880     render : function(){
19881         if(this.el){
19882             return;
19883         }
19884         var el = this.el = new Roo.Layer({
19885             cls: "x-menu",
19886             shadow:this.shadow,
19887             constrain: false,
19888             parentEl: this.parentEl || document.body,
19889             zindex:15000
19890         });
19891
19892         this.keyNav = new Roo.menu.MenuNav(this);
19893
19894         if(this.plain){
19895             el.addClass("x-menu-plain");
19896         }
19897         if(this.cls){
19898             el.addClass(this.cls);
19899         }
19900         // generic focus element
19901         this.focusEl = el.createChild({
19902             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19903         });
19904         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19905         ul.on("click", this.onClick, this);
19906         ul.on("mouseover", this.onMouseOver, this);
19907         ul.on("mouseout", this.onMouseOut, this);
19908         this.items.each(function(item){
19909             if (item.hidden) {
19910                 return;
19911             }
19912             
19913             var li = document.createElement("li");
19914             li.className = "x-menu-list-item";
19915             ul.dom.appendChild(li);
19916             item.render(li, this);
19917         }, this);
19918         this.ul = ul;
19919         this.autoWidth();
19920     },
19921
19922     // private
19923     autoWidth : function(){
19924         var el = this.el, ul = this.ul;
19925         if(!el){
19926             return;
19927         }
19928         var w = this.width;
19929         if(w){
19930             el.setWidth(w);
19931         }else if(Roo.isIE){
19932             el.setWidth(this.minWidth);
19933             var t = el.dom.offsetWidth; // force recalc
19934             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19935         }
19936     },
19937
19938     // private
19939     delayAutoWidth : function(){
19940         if(this.rendered){
19941             if(!this.awTask){
19942                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19943             }
19944             this.awTask.delay(20);
19945         }
19946     },
19947
19948     // private
19949     findTargetItem : function(e){
19950         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19951         if(t && t.menuItemId){
19952             return this.items.get(t.menuItemId);
19953         }
19954     },
19955
19956     // private
19957     onClick : function(e){
19958         var t;
19959         if(t = this.findTargetItem(e)){
19960             t.onClick(e);
19961             this.fireEvent("click", this, t, e);
19962         }
19963     },
19964
19965     // private
19966     setActiveItem : function(item, autoExpand){
19967         if(item != this.activeItem){
19968             if(this.activeItem){
19969                 this.activeItem.deactivate();
19970             }
19971             this.activeItem = item;
19972             item.activate(autoExpand);
19973         }else if(autoExpand){
19974             item.expandMenu();
19975         }
19976     },
19977
19978     // private
19979     tryActivate : function(start, step){
19980         var items = this.items;
19981         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19982             var item = items.get(i);
19983             if(!item.disabled && item.canActivate){
19984                 this.setActiveItem(item, false);
19985                 return item;
19986             }
19987         }
19988         return false;
19989     },
19990
19991     // private
19992     onMouseOver : function(e){
19993         var t;
19994         if(t = this.findTargetItem(e)){
19995             if(t.canActivate && !t.disabled){
19996                 this.setActiveItem(t, true);
19997             }
19998         }
19999         this.fireEvent("mouseover", this, e, t);
20000     },
20001
20002     // private
20003     onMouseOut : function(e){
20004         var t;
20005         if(t = this.findTargetItem(e)){
20006             if(t == this.activeItem && t.shouldDeactivate(e)){
20007                 this.activeItem.deactivate();
20008                 delete this.activeItem;
20009             }
20010         }
20011         this.fireEvent("mouseout", this, e, t);
20012     },
20013
20014     /**
20015      * Read-only.  Returns true if the menu is currently displayed, else false.
20016      * @type Boolean
20017      */
20018     isVisible : function(){
20019         return this.el && !this.hidden;
20020     },
20021
20022     /**
20023      * Displays this menu relative to another element
20024      * @param {String/HTMLElement/Roo.Element} element The element to align to
20025      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
20026      * the element (defaults to this.defaultAlign)
20027      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20028      */
20029     show : function(el, pos, parentMenu){
20030         this.parentMenu = parentMenu;
20031         if(!this.el){
20032             this.render();
20033         }
20034         this.fireEvent("beforeshow", this);
20035         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
20036     },
20037
20038     /**
20039      * Displays this menu at a specific xy position
20040      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
20041      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20042      */
20043     showAt : function(xy, parentMenu, /* private: */_e){
20044         this.parentMenu = parentMenu;
20045         if(!this.el){
20046             this.render();
20047         }
20048         if(_e !== false){
20049             this.fireEvent("beforeshow", this);
20050             xy = this.el.adjustForConstraints(xy);
20051         }
20052         this.el.setXY(xy);
20053         this.el.show();
20054         this.hidden = false;
20055         this.focus();
20056         this.fireEvent("show", this);
20057     },
20058
20059     focus : function(){
20060         if(!this.hidden){
20061             this.doFocus.defer(50, this);
20062         }
20063     },
20064
20065     doFocus : function(){
20066         if(!this.hidden){
20067             this.focusEl.focus();
20068         }
20069     },
20070
20071     /**
20072      * Hides this menu and optionally all parent menus
20073      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
20074      */
20075     hide : function(deep){
20076         if(this.el && this.isVisible()){
20077             this.fireEvent("beforehide", this);
20078             if(this.activeItem){
20079                 this.activeItem.deactivate();
20080                 this.activeItem = null;
20081             }
20082             this.el.hide();
20083             this.hidden = true;
20084             this.fireEvent("hide", this);
20085         }
20086         if(deep === true && this.parentMenu){
20087             this.parentMenu.hide(true);
20088         }
20089     },
20090
20091     /**
20092      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
20093      * Any of the following are valid:
20094      * <ul>
20095      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
20096      * <li>An HTMLElement object which will be converted to a menu item</li>
20097      * <li>A menu item config object that will be created as a new menu item</li>
20098      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
20099      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
20100      * </ul>
20101      * Usage:
20102      * <pre><code>
20103 // Create the menu
20104 var menu = new Roo.menu.Menu();
20105
20106 // Create a menu item to add by reference
20107 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20108
20109 // Add a bunch of items at once using different methods.
20110 // Only the last item added will be returned.
20111 var item = menu.add(
20112     menuItem,                // add existing item by ref
20113     'Dynamic Item',          // new TextItem
20114     '-',                     // new separator
20115     { text: 'Config Item' }  // new item by config
20116 );
20117 </code></pre>
20118      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20119      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20120      */
20121     add : function(){
20122         var a = arguments, l = a.length, item;
20123         for(var i = 0; i < l; i++){
20124             var el = a[i];
20125             if ((typeof(el) == "object") && el.xtype && el.xns) {
20126                 el = Roo.factory(el, Roo.menu);
20127             }
20128             
20129             if(el.render){ // some kind of Item
20130                 item = this.addItem(el);
20131             }else if(typeof el == "string"){ // string
20132                 if(el == "separator" || el == "-"){
20133                     item = this.addSeparator();
20134                 }else{
20135                     item = this.addText(el);
20136                 }
20137             }else if(el.tagName || el.el){ // element
20138                 item = this.addElement(el);
20139             }else if(typeof el == "object"){ // must be menu item config?
20140                 item = this.addMenuItem(el);
20141             }
20142         }
20143         return item;
20144     },
20145
20146     /**
20147      * Returns this menu's underlying {@link Roo.Element} object
20148      * @return {Roo.Element} The element
20149      */
20150     getEl : function(){
20151         if(!this.el){
20152             this.render();
20153         }
20154         return this.el;
20155     },
20156
20157     /**
20158      * Adds a separator bar to the menu
20159      * @return {Roo.menu.Item} The menu item that was added
20160      */
20161     addSeparator : function(){
20162         return this.addItem(new Roo.menu.Separator());
20163     },
20164
20165     /**
20166      * Adds an {@link Roo.Element} object to the menu
20167      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20168      * @return {Roo.menu.Item} The menu item that was added
20169      */
20170     addElement : function(el){
20171         return this.addItem(new Roo.menu.BaseItem(el));
20172     },
20173
20174     /**
20175      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20176      * @param {Roo.menu.Item} item The menu item to add
20177      * @return {Roo.menu.Item} The menu item that was added
20178      */
20179     addItem : function(item){
20180         this.items.add(item);
20181         if(this.ul){
20182             var li = document.createElement("li");
20183             li.className = "x-menu-list-item";
20184             this.ul.dom.appendChild(li);
20185             item.render(li, this);
20186             this.delayAutoWidth();
20187         }
20188         return item;
20189     },
20190
20191     /**
20192      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20193      * @param {Object} config A MenuItem config object
20194      * @return {Roo.menu.Item} The menu item that was added
20195      */
20196     addMenuItem : function(config){
20197         if(!(config instanceof Roo.menu.Item)){
20198             if(typeof config.checked == "boolean"){ // must be check menu item config?
20199                 config = new Roo.menu.CheckItem(config);
20200             }else{
20201                 config = new Roo.menu.Item(config);
20202             }
20203         }
20204         return this.addItem(config);
20205     },
20206
20207     /**
20208      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20209      * @param {String} text The text to display in the menu item
20210      * @return {Roo.menu.Item} The menu item that was added
20211      */
20212     addText : function(text){
20213         return this.addItem(new Roo.menu.TextItem({ text : text }));
20214     },
20215
20216     /**
20217      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20218      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20219      * @param {Roo.menu.Item} item The menu item to add
20220      * @return {Roo.menu.Item} The menu item that was added
20221      */
20222     insert : function(index, item){
20223         this.items.insert(index, item);
20224         if(this.ul){
20225             var li = document.createElement("li");
20226             li.className = "x-menu-list-item";
20227             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20228             item.render(li, this);
20229             this.delayAutoWidth();
20230         }
20231         return item;
20232     },
20233
20234     /**
20235      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20236      * @param {Roo.menu.Item} item The menu item to remove
20237      */
20238     remove : function(item){
20239         this.items.removeKey(item.id);
20240         item.destroy();
20241     },
20242
20243     /**
20244      * Removes and destroys all items in the menu
20245      */
20246     removeAll : function(){
20247         var f;
20248         while(f = this.items.first()){
20249             this.remove(f);
20250         }
20251     }
20252 });
20253
20254 // MenuNav is a private utility class used internally by the Menu
20255 Roo.menu.MenuNav = function(menu){
20256     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20257     this.scope = this.menu = menu;
20258 };
20259
20260 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20261     doRelay : function(e, h){
20262         var k = e.getKey();
20263         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20264             this.menu.tryActivate(0, 1);
20265             return false;
20266         }
20267         return h.call(this.scope || this, e, this.menu);
20268     },
20269
20270     up : function(e, m){
20271         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20272             m.tryActivate(m.items.length-1, -1);
20273         }
20274     },
20275
20276     down : function(e, m){
20277         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20278             m.tryActivate(0, 1);
20279         }
20280     },
20281
20282     right : function(e, m){
20283         if(m.activeItem){
20284             m.activeItem.expandMenu(true);
20285         }
20286     },
20287
20288     left : function(e, m){
20289         m.hide();
20290         if(m.parentMenu && m.parentMenu.activeItem){
20291             m.parentMenu.activeItem.activate();
20292         }
20293     },
20294
20295     enter : function(e, m){
20296         if(m.activeItem){
20297             e.stopPropagation();
20298             m.activeItem.onClick(e);
20299             m.fireEvent("click", this, m.activeItem);
20300             return true;
20301         }
20302     }
20303 });/*
20304  * Based on:
20305  * Ext JS Library 1.1.1
20306  * Copyright(c) 2006-2007, Ext JS, LLC.
20307  *
20308  * Originally Released Under LGPL - original licence link has changed is not relivant.
20309  *
20310  * Fork - LGPL
20311  * <script type="text/javascript">
20312  */
20313  
20314 /**
20315  * @class Roo.menu.MenuMgr
20316  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20317  * @singleton
20318  */
20319 Roo.menu.MenuMgr = function(){
20320    var menus, active, groups = {}, attached = false, lastShow = new Date();
20321
20322    // private - called when first menu is created
20323    function init(){
20324        menus = {};
20325        active = new Roo.util.MixedCollection();
20326        Roo.get(document).addKeyListener(27, function(){
20327            if(active.length > 0){
20328                hideAll();
20329            }
20330        });
20331    }
20332
20333    // private
20334    function hideAll(){
20335        if(active && active.length > 0){
20336            var c = active.clone();
20337            c.each(function(m){
20338                m.hide();
20339            });
20340        }
20341    }
20342
20343    // private
20344    function onHide(m){
20345        active.remove(m);
20346        if(active.length < 1){
20347            Roo.get(document).un("mousedown", onMouseDown);
20348            attached = false;
20349        }
20350    }
20351
20352    // private
20353    function onShow(m){
20354        var last = active.last();
20355        lastShow = new Date();
20356        active.add(m);
20357        if(!attached){
20358            Roo.get(document).on("mousedown", onMouseDown);
20359            attached = true;
20360        }
20361        if(m.parentMenu){
20362           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20363           m.parentMenu.activeChild = m;
20364        }else if(last && last.isVisible()){
20365           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20366        }
20367    }
20368
20369    // private
20370    function onBeforeHide(m){
20371        if(m.activeChild){
20372            m.activeChild.hide();
20373        }
20374        if(m.autoHideTimer){
20375            clearTimeout(m.autoHideTimer);
20376            delete m.autoHideTimer;
20377        }
20378    }
20379
20380    // private
20381    function onBeforeShow(m){
20382        var pm = m.parentMenu;
20383        if(!pm && !m.allowOtherMenus){
20384            hideAll();
20385        }else if(pm && pm.activeChild && active != m){
20386            pm.activeChild.hide();
20387        }
20388    }
20389
20390    // private
20391    function onMouseDown(e){
20392        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20393            hideAll();
20394        }
20395    }
20396
20397    // private
20398    function onBeforeCheck(mi, state){
20399        if(state){
20400            var g = groups[mi.group];
20401            for(var i = 0, l = g.length; i < l; i++){
20402                if(g[i] != mi){
20403                    g[i].setChecked(false);
20404                }
20405            }
20406        }
20407    }
20408
20409    return {
20410
20411        /**
20412         * Hides all menus that are currently visible
20413         */
20414        hideAll : function(){
20415             hideAll();  
20416        },
20417
20418        // private
20419        register : function(menu){
20420            if(!menus){
20421                init();
20422            }
20423            menus[menu.id] = menu;
20424            menu.on("beforehide", onBeforeHide);
20425            menu.on("hide", onHide);
20426            menu.on("beforeshow", onBeforeShow);
20427            menu.on("show", onShow);
20428            var g = menu.group;
20429            if(g && menu.events["checkchange"]){
20430                if(!groups[g]){
20431                    groups[g] = [];
20432                }
20433                groups[g].push(menu);
20434                menu.on("checkchange", onCheck);
20435            }
20436        },
20437
20438         /**
20439          * Returns a {@link Roo.menu.Menu} object
20440          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20441          * be used to generate and return a new Menu instance.
20442          */
20443        get : function(menu){
20444            if(typeof menu == "string"){ // menu id
20445                return menus[menu];
20446            }else if(menu.events){  // menu instance
20447                return menu;
20448            }else if(typeof menu.length == 'number'){ // array of menu items?
20449                return new Roo.menu.Menu({items:menu});
20450            }else{ // otherwise, must be a config
20451                return new Roo.menu.Menu(menu);
20452            }
20453        },
20454
20455        // private
20456        unregister : function(menu){
20457            delete menus[menu.id];
20458            menu.un("beforehide", onBeforeHide);
20459            menu.un("hide", onHide);
20460            menu.un("beforeshow", onBeforeShow);
20461            menu.un("show", onShow);
20462            var g = menu.group;
20463            if(g && menu.events["checkchange"]){
20464                groups[g].remove(menu);
20465                menu.un("checkchange", onCheck);
20466            }
20467        },
20468
20469        // private
20470        registerCheckable : function(menuItem){
20471            var g = menuItem.group;
20472            if(g){
20473                if(!groups[g]){
20474                    groups[g] = [];
20475                }
20476                groups[g].push(menuItem);
20477                menuItem.on("beforecheckchange", onBeforeCheck);
20478            }
20479        },
20480
20481        // private
20482        unregisterCheckable : function(menuItem){
20483            var g = menuItem.group;
20484            if(g){
20485                groups[g].remove(menuItem);
20486                menuItem.un("beforecheckchange", onBeforeCheck);
20487            }
20488        }
20489    };
20490 }();/*
20491  * Based on:
20492  * Ext JS Library 1.1.1
20493  * Copyright(c) 2006-2007, Ext JS, LLC.
20494  *
20495  * Originally Released Under LGPL - original licence link has changed is not relivant.
20496  *
20497  * Fork - LGPL
20498  * <script type="text/javascript">
20499  */
20500  
20501
20502 /**
20503  * @class Roo.menu.BaseItem
20504  * @extends Roo.Component
20505  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20506  * management and base configuration options shared by all menu components.
20507  * @constructor
20508  * Creates a new BaseItem
20509  * @param {Object} config Configuration options
20510  */
20511 Roo.menu.BaseItem = function(config){
20512     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20513
20514     this.addEvents({
20515         /**
20516          * @event click
20517          * Fires when this item is clicked
20518          * @param {Roo.menu.BaseItem} this
20519          * @param {Roo.EventObject} e
20520          */
20521         click: true,
20522         /**
20523          * @event activate
20524          * Fires when this item is activated
20525          * @param {Roo.menu.BaseItem} this
20526          */
20527         activate : true,
20528         /**
20529          * @event deactivate
20530          * Fires when this item is deactivated
20531          * @param {Roo.menu.BaseItem} this
20532          */
20533         deactivate : true
20534     });
20535
20536     if(this.handler){
20537         this.on("click", this.handler, this.scope, true);
20538     }
20539 };
20540
20541 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20542     /**
20543      * @cfg {Function} handler
20544      * A function that will handle the click event of this menu item (defaults to undefined)
20545      */
20546     /**
20547      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20548      */
20549     canActivate : false,
20550     
20551      /**
20552      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
20553      */
20554     hidden: false,
20555     
20556     /**
20557      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20558      */
20559     activeClass : "x-menu-item-active",
20560     /**
20561      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20562      */
20563     hideOnClick : true,
20564     /**
20565      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20566      */
20567     hideDelay : 100,
20568
20569     // private
20570     ctype: "Roo.menu.BaseItem",
20571
20572     // private
20573     actionMode : "container",
20574
20575     // private
20576     render : function(container, parentMenu){
20577         this.parentMenu = parentMenu;
20578         Roo.menu.BaseItem.superclass.render.call(this, container);
20579         this.container.menuItemId = this.id;
20580     },
20581
20582     // private
20583     onRender : function(container, position){
20584         this.el = Roo.get(this.el);
20585         container.dom.appendChild(this.el.dom);
20586     },
20587
20588     // private
20589     onClick : function(e){
20590         if(!this.disabled && this.fireEvent("click", this, e) !== false
20591                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20592             this.handleClick(e);
20593         }else{
20594             e.stopEvent();
20595         }
20596     },
20597
20598     // private
20599     activate : function(){
20600         if(this.disabled){
20601             return false;
20602         }
20603         var li = this.container;
20604         li.addClass(this.activeClass);
20605         this.region = li.getRegion().adjust(2, 2, -2, -2);
20606         this.fireEvent("activate", this);
20607         return true;
20608     },
20609
20610     // private
20611     deactivate : function(){
20612         this.container.removeClass(this.activeClass);
20613         this.fireEvent("deactivate", this);
20614     },
20615
20616     // private
20617     shouldDeactivate : function(e){
20618         return !this.region || !this.region.contains(e.getPoint());
20619     },
20620
20621     // private
20622     handleClick : function(e){
20623         if(this.hideOnClick){
20624             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20625         }
20626     },
20627
20628     // private
20629     expandMenu : function(autoActivate){
20630         // do nothing
20631     },
20632
20633     // private
20634     hideMenu : function(){
20635         // do nothing
20636     }
20637 });/*
20638  * Based on:
20639  * Ext JS Library 1.1.1
20640  * Copyright(c) 2006-2007, Ext JS, LLC.
20641  *
20642  * Originally Released Under LGPL - original licence link has changed is not relivant.
20643  *
20644  * Fork - LGPL
20645  * <script type="text/javascript">
20646  */
20647  
20648 /**
20649  * @class Roo.menu.Adapter
20650  * @extends Roo.menu.BaseItem
20651  * 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.
20652  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20653  * @constructor
20654  * Creates a new Adapter
20655  * @param {Object} config Configuration options
20656  */
20657 Roo.menu.Adapter = function(component, config){
20658     Roo.menu.Adapter.superclass.constructor.call(this, config);
20659     this.component = component;
20660 };
20661 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20662     // private
20663     canActivate : true,
20664
20665     // private
20666     onRender : function(container, position){
20667         this.component.render(container);
20668         this.el = this.component.getEl();
20669     },
20670
20671     // private
20672     activate : function(){
20673         if(this.disabled){
20674             return false;
20675         }
20676         this.component.focus();
20677         this.fireEvent("activate", this);
20678         return true;
20679     },
20680
20681     // private
20682     deactivate : function(){
20683         this.fireEvent("deactivate", this);
20684     },
20685
20686     // private
20687     disable : function(){
20688         this.component.disable();
20689         Roo.menu.Adapter.superclass.disable.call(this);
20690     },
20691
20692     // private
20693     enable : function(){
20694         this.component.enable();
20695         Roo.menu.Adapter.superclass.enable.call(this);
20696     }
20697 });/*
20698  * Based on:
20699  * Ext JS Library 1.1.1
20700  * Copyright(c) 2006-2007, Ext JS, LLC.
20701  *
20702  * Originally Released Under LGPL - original licence link has changed is not relivant.
20703  *
20704  * Fork - LGPL
20705  * <script type="text/javascript">
20706  */
20707
20708 /**
20709  * @class Roo.menu.TextItem
20710  * @extends Roo.menu.BaseItem
20711  * Adds a static text string to a menu, usually used as either a heading or group separator.
20712  * Note: old style constructor with text is still supported.
20713  * 
20714  * @constructor
20715  * Creates a new TextItem
20716  * @param {Object} cfg Configuration
20717  */
20718 Roo.menu.TextItem = function(cfg){
20719     if (typeof(cfg) == 'string') {
20720         this.text = cfg;
20721     } else {
20722         Roo.apply(this,cfg);
20723     }
20724     
20725     Roo.menu.TextItem.superclass.constructor.call(this);
20726 };
20727
20728 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20729     /**
20730      * @cfg {Boolean} text Text to show on item.
20731      */
20732     text : '',
20733     
20734     /**
20735      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20736      */
20737     hideOnClick : false,
20738     /**
20739      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20740      */
20741     itemCls : "x-menu-text",
20742
20743     // private
20744     onRender : function(){
20745         var s = document.createElement("span");
20746         s.className = this.itemCls;
20747         s.innerHTML = this.text;
20748         this.el = s;
20749         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20750     }
20751 });/*
20752  * Based on:
20753  * Ext JS Library 1.1.1
20754  * Copyright(c) 2006-2007, Ext JS, LLC.
20755  *
20756  * Originally Released Under LGPL - original licence link has changed is not relivant.
20757  *
20758  * Fork - LGPL
20759  * <script type="text/javascript">
20760  */
20761
20762 /**
20763  * @class Roo.menu.Separator
20764  * @extends Roo.menu.BaseItem
20765  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20766  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20767  * @constructor
20768  * @param {Object} config Configuration options
20769  */
20770 Roo.menu.Separator = function(config){
20771     Roo.menu.Separator.superclass.constructor.call(this, config);
20772 };
20773
20774 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20775     /**
20776      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20777      */
20778     itemCls : "x-menu-sep",
20779     /**
20780      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20781      */
20782     hideOnClick : false,
20783
20784     // private
20785     onRender : function(li){
20786         var s = document.createElement("span");
20787         s.className = this.itemCls;
20788         s.innerHTML = "&#160;";
20789         this.el = s;
20790         li.addClass("x-menu-sep-li");
20791         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20792     }
20793 });/*
20794  * Based on:
20795  * Ext JS Library 1.1.1
20796  * Copyright(c) 2006-2007, Ext JS, LLC.
20797  *
20798  * Originally Released Under LGPL - original licence link has changed is not relivant.
20799  *
20800  * Fork - LGPL
20801  * <script type="text/javascript">
20802  */
20803 /**
20804  * @class Roo.menu.Item
20805  * @extends Roo.menu.BaseItem
20806  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20807  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20808  * activation and click handling.
20809  * @constructor
20810  * Creates a new Item
20811  * @param {Object} config Configuration options
20812  */
20813 Roo.menu.Item = function(config){
20814     Roo.menu.Item.superclass.constructor.call(this, config);
20815     if(this.menu){
20816         this.menu = Roo.menu.MenuMgr.get(this.menu);
20817     }
20818 };
20819 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20820     
20821     /**
20822      * @cfg {String} text
20823      * The text to show on the menu item.
20824      */
20825     text: '',
20826      /**
20827      * @cfg {String} HTML to render in menu
20828      * The text to show on the menu item (HTML version).
20829      */
20830     html: '',
20831     /**
20832      * @cfg {String} icon
20833      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20834      */
20835     icon: undefined,
20836     /**
20837      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20838      */
20839     itemCls : "x-menu-item",
20840     /**
20841      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20842      */
20843     canActivate : true,
20844     /**
20845      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20846      */
20847     showDelay: 200,
20848     // doc'd in BaseItem
20849     hideDelay: 200,
20850
20851     // private
20852     ctype: "Roo.menu.Item",
20853     
20854     // private
20855     onRender : function(container, position){
20856         var el = document.createElement("a");
20857         el.hideFocus = true;
20858         el.unselectable = "on";
20859         el.href = this.href || "#";
20860         if(this.hrefTarget){
20861             el.target = this.hrefTarget;
20862         }
20863         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20864         
20865         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20866         
20867         el.innerHTML = String.format(
20868                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20869                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20870         this.el = el;
20871         Roo.menu.Item.superclass.onRender.call(this, container, position);
20872     },
20873
20874     /**
20875      * Sets the text to display in this menu item
20876      * @param {String} text The text to display
20877      * @param {Boolean} isHTML true to indicate text is pure html.
20878      */
20879     setText : function(text, isHTML){
20880         if (isHTML) {
20881             this.html = text;
20882         } else {
20883             this.text = text;
20884             this.html = '';
20885         }
20886         if(this.rendered){
20887             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20888      
20889             this.el.update(String.format(
20890                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20891                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20892             this.parentMenu.autoWidth();
20893         }
20894     },
20895
20896     // private
20897     handleClick : function(e){
20898         if(!this.href){ // if no link defined, stop the event automatically
20899             e.stopEvent();
20900         }
20901         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20902     },
20903
20904     // private
20905     activate : function(autoExpand){
20906         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20907             this.focus();
20908             if(autoExpand){
20909                 this.expandMenu();
20910             }
20911         }
20912         return true;
20913     },
20914
20915     // private
20916     shouldDeactivate : function(e){
20917         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20918             if(this.menu && this.menu.isVisible()){
20919                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20920             }
20921             return true;
20922         }
20923         return false;
20924     },
20925
20926     // private
20927     deactivate : function(){
20928         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20929         this.hideMenu();
20930     },
20931
20932     // private
20933     expandMenu : function(autoActivate){
20934         if(!this.disabled && this.menu){
20935             clearTimeout(this.hideTimer);
20936             delete this.hideTimer;
20937             if(!this.menu.isVisible() && !this.showTimer){
20938                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20939             }else if (this.menu.isVisible() && autoActivate){
20940                 this.menu.tryActivate(0, 1);
20941             }
20942         }
20943     },
20944
20945     // private
20946     deferExpand : function(autoActivate){
20947         delete this.showTimer;
20948         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20949         if(autoActivate){
20950             this.menu.tryActivate(0, 1);
20951         }
20952     },
20953
20954     // private
20955     hideMenu : function(){
20956         clearTimeout(this.showTimer);
20957         delete this.showTimer;
20958         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20959             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20960         }
20961     },
20962
20963     // private
20964     deferHide : function(){
20965         delete this.hideTimer;
20966         this.menu.hide();
20967     }
20968 });/*
20969  * Based on:
20970  * Ext JS Library 1.1.1
20971  * Copyright(c) 2006-2007, Ext JS, LLC.
20972  *
20973  * Originally Released Under LGPL - original licence link has changed is not relivant.
20974  *
20975  * Fork - LGPL
20976  * <script type="text/javascript">
20977  */
20978  
20979 /**
20980  * @class Roo.menu.CheckItem
20981  * @extends Roo.menu.Item
20982  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20983  * @constructor
20984  * Creates a new CheckItem
20985  * @param {Object} config Configuration options
20986  */
20987 Roo.menu.CheckItem = function(config){
20988     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20989     this.addEvents({
20990         /**
20991          * @event beforecheckchange
20992          * Fires before the checked value is set, providing an opportunity to cancel if needed
20993          * @param {Roo.menu.CheckItem} this
20994          * @param {Boolean} checked The new checked value that will be set
20995          */
20996         "beforecheckchange" : true,
20997         /**
20998          * @event checkchange
20999          * Fires after the checked value has been set
21000          * @param {Roo.menu.CheckItem} this
21001          * @param {Boolean} checked The checked value that was set
21002          */
21003         "checkchange" : true
21004     });
21005     if(this.checkHandler){
21006         this.on('checkchange', this.checkHandler, this.scope);
21007     }
21008 };
21009 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
21010     /**
21011      * @cfg {String} group
21012      * All check items with the same group name will automatically be grouped into a single-select
21013      * radio button group (defaults to '')
21014      */
21015     /**
21016      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
21017      */
21018     itemCls : "x-menu-item x-menu-check-item",
21019     /**
21020      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
21021      */
21022     groupClass : "x-menu-group-item",
21023
21024     /**
21025      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
21026      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
21027      * initialized with checked = true will be rendered as checked.
21028      */
21029     checked: false,
21030
21031     // private
21032     ctype: "Roo.menu.CheckItem",
21033
21034     // private
21035     onRender : function(c){
21036         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
21037         if(this.group){
21038             this.el.addClass(this.groupClass);
21039         }
21040         Roo.menu.MenuMgr.registerCheckable(this);
21041         if(this.checked){
21042             this.checked = false;
21043             this.setChecked(true, true);
21044         }
21045     },
21046
21047     // private
21048     destroy : function(){
21049         if(this.rendered){
21050             Roo.menu.MenuMgr.unregisterCheckable(this);
21051         }
21052         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
21053     },
21054
21055     /**
21056      * Set the checked state of this item
21057      * @param {Boolean} checked The new checked value
21058      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
21059      */
21060     setChecked : function(state, suppressEvent){
21061         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
21062             if(this.container){
21063                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
21064             }
21065             this.checked = state;
21066             if(suppressEvent !== true){
21067                 this.fireEvent("checkchange", this, state);
21068             }
21069         }
21070     },
21071
21072     // private
21073     handleClick : function(e){
21074        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
21075            this.setChecked(!this.checked);
21076        }
21077        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
21078     }
21079 });/*
21080  * Based on:
21081  * Ext JS Library 1.1.1
21082  * Copyright(c) 2006-2007, Ext JS, LLC.
21083  *
21084  * Originally Released Under LGPL - original licence link has changed is not relivant.
21085  *
21086  * Fork - LGPL
21087  * <script type="text/javascript">
21088  */
21089  
21090 /**
21091  * @class Roo.menu.DateItem
21092  * @extends Roo.menu.Adapter
21093  * A menu item that wraps the {@link Roo.DatPicker} component.
21094  * @constructor
21095  * Creates a new DateItem
21096  * @param {Object} config Configuration options
21097  */
21098 Roo.menu.DateItem = function(config){
21099     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
21100     /** The Roo.DatePicker object @type Roo.DatePicker */
21101     this.picker = this.component;
21102     this.addEvents({select: true});
21103     
21104     this.picker.on("render", function(picker){
21105         picker.getEl().swallowEvent("click");
21106         picker.container.addClass("x-menu-date-item");
21107     });
21108
21109     this.picker.on("select", this.onSelect, this);
21110 };
21111
21112 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21113     // private
21114     onSelect : function(picker, date){
21115         this.fireEvent("select", this, date, picker);
21116         Roo.menu.DateItem.superclass.handleClick.call(this);
21117     }
21118 });/*
21119  * Based on:
21120  * Ext JS Library 1.1.1
21121  * Copyright(c) 2006-2007, Ext JS, LLC.
21122  *
21123  * Originally Released Under LGPL - original licence link has changed is not relivant.
21124  *
21125  * Fork - LGPL
21126  * <script type="text/javascript">
21127  */
21128  
21129 /**
21130  * @class Roo.menu.ColorItem
21131  * @extends Roo.menu.Adapter
21132  * A menu item that wraps the {@link Roo.ColorPalette} component.
21133  * @constructor
21134  * Creates a new ColorItem
21135  * @param {Object} config Configuration options
21136  */
21137 Roo.menu.ColorItem = function(config){
21138     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21139     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21140     this.palette = this.component;
21141     this.relayEvents(this.palette, ["select"]);
21142     if(this.selectHandler){
21143         this.on('select', this.selectHandler, this.scope);
21144     }
21145 };
21146 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21147  * Based on:
21148  * Ext JS Library 1.1.1
21149  * Copyright(c) 2006-2007, Ext JS, LLC.
21150  *
21151  * Originally Released Under LGPL - original licence link has changed is not relivant.
21152  *
21153  * Fork - LGPL
21154  * <script type="text/javascript">
21155  */
21156  
21157
21158 /**
21159  * @class Roo.menu.DateMenu
21160  * @extends Roo.menu.Menu
21161  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21162  * @constructor
21163  * Creates a new DateMenu
21164  * @param {Object} config Configuration options
21165  */
21166 Roo.menu.DateMenu = function(config){
21167     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21168     this.plain = true;
21169     var di = new Roo.menu.DateItem(config);
21170     this.add(di);
21171     /**
21172      * The {@link Roo.DatePicker} instance for this DateMenu
21173      * @type DatePicker
21174      */
21175     this.picker = di.picker;
21176     /**
21177      * @event select
21178      * @param {DatePicker} picker
21179      * @param {Date} date
21180      */
21181     this.relayEvents(di, ["select"]);
21182     this.on('beforeshow', function(){
21183         if(this.picker){
21184             this.picker.hideMonthPicker(false);
21185         }
21186     }, this);
21187 };
21188 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21189     cls:'x-date-menu'
21190 });/*
21191  * Based on:
21192  * Ext JS Library 1.1.1
21193  * Copyright(c) 2006-2007, Ext JS, LLC.
21194  *
21195  * Originally Released Under LGPL - original licence link has changed is not relivant.
21196  *
21197  * Fork - LGPL
21198  * <script type="text/javascript">
21199  */
21200  
21201
21202 /**
21203  * @class Roo.menu.ColorMenu
21204  * @extends Roo.menu.Menu
21205  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21206  * @constructor
21207  * Creates a new ColorMenu
21208  * @param {Object} config Configuration options
21209  */
21210 Roo.menu.ColorMenu = function(config){
21211     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21212     this.plain = true;
21213     var ci = new Roo.menu.ColorItem(config);
21214     this.add(ci);
21215     /**
21216      * The {@link Roo.ColorPalette} instance for this ColorMenu
21217      * @type ColorPalette
21218      */
21219     this.palette = ci.palette;
21220     /**
21221      * @event select
21222      * @param {ColorPalette} palette
21223      * @param {String} color
21224      */
21225     this.relayEvents(ci, ["select"]);
21226 };
21227 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21228  * Based on:
21229  * Ext JS Library 1.1.1
21230  * Copyright(c) 2006-2007, Ext JS, LLC.
21231  *
21232  * Originally Released Under LGPL - original licence link has changed is not relivant.
21233  *
21234  * Fork - LGPL
21235  * <script type="text/javascript">
21236  */
21237  
21238 /**
21239  * @class Roo.form.Field
21240  * @extends Roo.BoxComponent
21241  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21242  * @constructor
21243  * Creates a new Field
21244  * @param {Object} config Configuration options
21245  */
21246 Roo.form.Field = function(config){
21247     Roo.form.Field.superclass.constructor.call(this, config);
21248 };
21249
21250 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21251     /**
21252      * @cfg {String} fieldLabel Label to use when rendering a form.
21253      */
21254        /**
21255      * @cfg {String} qtip Mouse over tip
21256      */
21257      
21258     /**
21259      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21260      */
21261     invalidClass : "x-form-invalid",
21262     /**
21263      * @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")
21264      */
21265     invalidText : "The value in this field is invalid",
21266     /**
21267      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21268      */
21269     focusClass : "x-form-focus",
21270     /**
21271      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21272       automatic validation (defaults to "keyup").
21273      */
21274     validationEvent : "keyup",
21275     /**
21276      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21277      */
21278     validateOnBlur : true,
21279     /**
21280      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21281      */
21282     validationDelay : 250,
21283     /**
21284      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21285      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21286      */
21287     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21288     /**
21289      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21290      */
21291     fieldClass : "x-form-field",
21292     /**
21293      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21294      *<pre>
21295 Value         Description
21296 -----------   ----------------------------------------------------------------------
21297 qtip          Display a quick tip when the user hovers over the field
21298 title         Display a default browser title attribute popup
21299 under         Add a block div beneath the field containing the error text
21300 side          Add an error icon to the right of the field with a popup on hover
21301 [element id]  Add the error text directly to the innerHTML of the specified element
21302 </pre>
21303      */
21304     msgTarget : 'qtip',
21305     /**
21306      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21307      */
21308     msgFx : 'normal',
21309
21310     /**
21311      * @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.
21312      */
21313     readOnly : false,
21314
21315     /**
21316      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21317      */
21318     disabled : false,
21319
21320     /**
21321      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21322      */
21323     inputType : undefined,
21324     
21325     /**
21326      * @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).
21327          */
21328         tabIndex : undefined,
21329         
21330     // private
21331     isFormField : true,
21332
21333     // private
21334     hasFocus : false,
21335     /**
21336      * @property {Roo.Element} fieldEl
21337      * Element Containing the rendered Field (with label etc.)
21338      */
21339     /**
21340      * @cfg {Mixed} value A value to initialize this field with.
21341      */
21342     value : undefined,
21343
21344     /**
21345      * @cfg {String} name The field's HTML name attribute.
21346      */
21347     /**
21348      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21349      */
21350
21351         // private ??
21352         initComponent : function(){
21353         Roo.form.Field.superclass.initComponent.call(this);
21354         this.addEvents({
21355             /**
21356              * @event focus
21357              * Fires when this field receives input focus.
21358              * @param {Roo.form.Field} this
21359              */
21360             focus : true,
21361             /**
21362              * @event blur
21363              * Fires when this field loses input focus.
21364              * @param {Roo.form.Field} this
21365              */
21366             blur : true,
21367             /**
21368              * @event specialkey
21369              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21370              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21371              * @param {Roo.form.Field} this
21372              * @param {Roo.EventObject} e The event object
21373              */
21374             specialkey : true,
21375             /**
21376              * @event change
21377              * Fires just before the field blurs if the field value has changed.
21378              * @param {Roo.form.Field} this
21379              * @param {Mixed} newValue The new value
21380              * @param {Mixed} oldValue The original value
21381              */
21382             change : true,
21383             /**
21384              * @event invalid
21385              * Fires after the field has been marked as invalid.
21386              * @param {Roo.form.Field} this
21387              * @param {String} msg The validation message
21388              */
21389             invalid : true,
21390             /**
21391              * @event valid
21392              * Fires after the field has been validated with no errors.
21393              * @param {Roo.form.Field} this
21394              */
21395             valid : true,
21396              /**
21397              * @event keyup
21398              * Fires after the key up
21399              * @param {Roo.form.Field} this
21400              * @param {Roo.EventObject}  e The event Object
21401              */
21402             keyup : true
21403         });
21404     },
21405
21406     /**
21407      * Returns the name attribute of the field if available
21408      * @return {String} name The field name
21409      */
21410     getName: function(){
21411          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21412     },
21413
21414     // private
21415     onRender : function(ct, position){
21416         Roo.form.Field.superclass.onRender.call(this, ct, position);
21417         if(!this.el){
21418             var cfg = this.getAutoCreate();
21419             if(!cfg.name){
21420                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
21421             }
21422             if (!cfg.name.length) {
21423                 delete cfg.name;
21424             }
21425             if(this.inputType){
21426                 cfg.type = this.inputType;
21427             }
21428             this.el = ct.createChild(cfg, position);
21429         }
21430         var type = this.el.dom.type;
21431         if(type){
21432             if(type == 'password'){
21433                 type = 'text';
21434             }
21435             this.el.addClass('x-form-'+type);
21436         }
21437         if(this.readOnly){
21438             this.el.dom.readOnly = true;
21439         }
21440         if(this.tabIndex !== undefined){
21441             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21442         }
21443
21444         this.el.addClass([this.fieldClass, this.cls]);
21445         this.initValue();
21446     },
21447
21448     /**
21449      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21450      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21451      * @return {Roo.form.Field} this
21452      */
21453     applyTo : function(target){
21454         this.allowDomMove = false;
21455         this.el = Roo.get(target);
21456         this.render(this.el.dom.parentNode);
21457         return this;
21458     },
21459
21460     // private
21461     initValue : function(){
21462         if(this.value !== undefined){
21463             this.setValue(this.value);
21464         }else if(this.el.dom.value.length > 0){
21465             this.setValue(this.el.dom.value);
21466         }
21467     },
21468
21469     /**
21470      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21471      */
21472     isDirty : function() {
21473         if(this.disabled) {
21474             return false;
21475         }
21476         return String(this.getValue()) !== String(this.originalValue);
21477     },
21478
21479     // private
21480     afterRender : function(){
21481         Roo.form.Field.superclass.afterRender.call(this);
21482         this.initEvents();
21483     },
21484
21485     // private
21486     fireKey : function(e){
21487         //Roo.log('field ' + e.getKey());
21488         if(e.isNavKeyPress()){
21489             this.fireEvent("specialkey", this, e);
21490         }
21491     },
21492
21493     /**
21494      * Resets the current field value to the originally loaded value and clears any validation messages
21495      */
21496     reset : function(){
21497         this.setValue(this.originalValue);
21498         this.clearInvalid();
21499     },
21500
21501     // private
21502     initEvents : function(){
21503         // safari killled keypress - so keydown is now used..
21504         this.el.on("keydown" , this.fireKey,  this);
21505         this.el.on("focus", this.onFocus,  this);
21506         this.el.on("blur", this.onBlur,  this);
21507         this.el.relayEvent('keyup', this);
21508
21509         // reference to original value for reset
21510         this.originalValue = this.getValue();
21511     },
21512
21513     // private
21514     onFocus : function(){
21515         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21516             this.el.addClass(this.focusClass);
21517         }
21518         if(!this.hasFocus){
21519             this.hasFocus = true;
21520             this.startValue = this.getValue();
21521             this.fireEvent("focus", this);
21522         }
21523     },
21524
21525     beforeBlur : Roo.emptyFn,
21526
21527     // private
21528     onBlur : function(){
21529         this.beforeBlur();
21530         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21531             this.el.removeClass(this.focusClass);
21532         }
21533         this.hasFocus = false;
21534         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21535             this.validate();
21536         }
21537         var v = this.getValue();
21538         if(String(v) !== String(this.startValue)){
21539             this.fireEvent('change', this, v, this.startValue);
21540         }
21541         this.fireEvent("blur", this);
21542     },
21543
21544     /**
21545      * Returns whether or not the field value is currently valid
21546      * @param {Boolean} preventMark True to disable marking the field invalid
21547      * @return {Boolean} True if the value is valid, else false
21548      */
21549     isValid : function(preventMark){
21550         if(this.disabled){
21551             return true;
21552         }
21553         var restore = this.preventMark;
21554         this.preventMark = preventMark === true;
21555         var v = this.validateValue(this.processValue(this.getRawValue()));
21556         this.preventMark = restore;
21557         return v;
21558     },
21559
21560     /**
21561      * Validates the field value
21562      * @return {Boolean} True if the value is valid, else false
21563      */
21564     validate : function(){
21565         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21566             this.clearInvalid();
21567             return true;
21568         }
21569         return false;
21570     },
21571
21572     processValue : function(value){
21573         return value;
21574     },
21575
21576     // private
21577     // Subclasses should provide the validation implementation by overriding this
21578     validateValue : function(value){
21579         return true;
21580     },
21581
21582     /**
21583      * Mark this field as invalid
21584      * @param {String} msg The validation message
21585      */
21586     markInvalid : function(msg){
21587         if(!this.rendered || this.preventMark){ // not rendered
21588             return;
21589         }
21590         this.el.addClass(this.invalidClass);
21591         msg = msg || this.invalidText;
21592         switch(this.msgTarget){
21593             case 'qtip':
21594                 this.el.dom.qtip = msg;
21595                 this.el.dom.qclass = 'x-form-invalid-tip';
21596                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21597                     Roo.QuickTips.enable();
21598                 }
21599                 break;
21600             case 'title':
21601                 this.el.dom.title = msg;
21602                 break;
21603             case 'under':
21604                 if(!this.errorEl){
21605                     var elp = this.el.findParent('.x-form-element', 5, true);
21606                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21607                     this.errorEl.setWidth(elp.getWidth(true)-20);
21608                 }
21609                 this.errorEl.update(msg);
21610                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21611                 break;
21612             case 'side':
21613                 if(!this.errorIcon){
21614                     var elp = this.el.findParent('.x-form-element', 5, true);
21615                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21616                 }
21617                 this.alignErrorIcon();
21618                 this.errorIcon.dom.qtip = msg;
21619                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21620                 this.errorIcon.show();
21621                 this.on('resize', this.alignErrorIcon, this);
21622                 break;
21623             default:
21624                 var t = Roo.getDom(this.msgTarget);
21625                 t.innerHTML = msg;
21626                 t.style.display = this.msgDisplay;
21627                 break;
21628         }
21629         this.fireEvent('invalid', this, msg);
21630     },
21631
21632     // private
21633     alignErrorIcon : function(){
21634         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21635     },
21636
21637     /**
21638      * Clear any invalid styles/messages for this field
21639      */
21640     clearInvalid : function(){
21641         if(!this.rendered || this.preventMark){ // not rendered
21642             return;
21643         }
21644         this.el.removeClass(this.invalidClass);
21645         switch(this.msgTarget){
21646             case 'qtip':
21647                 this.el.dom.qtip = '';
21648                 break;
21649             case 'title':
21650                 this.el.dom.title = '';
21651                 break;
21652             case 'under':
21653                 if(this.errorEl){
21654                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21655                 }
21656                 break;
21657             case 'side':
21658                 if(this.errorIcon){
21659                     this.errorIcon.dom.qtip = '';
21660                     this.errorIcon.hide();
21661                     this.un('resize', this.alignErrorIcon, this);
21662                 }
21663                 break;
21664             default:
21665                 var t = Roo.getDom(this.msgTarget);
21666                 t.innerHTML = '';
21667                 t.style.display = 'none';
21668                 break;
21669         }
21670         this.fireEvent('valid', this);
21671     },
21672
21673     /**
21674      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21675      * @return {Mixed} value The field value
21676      */
21677     getRawValue : function(){
21678         var v = this.el.getValue();
21679         
21680         return v;
21681     },
21682
21683     /**
21684      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21685      * @return {Mixed} value The field value
21686      */
21687     getValue : function(){
21688         var v = this.el.getValue();
21689          
21690         return v;
21691     },
21692
21693     /**
21694      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21695      * @param {Mixed} value The value to set
21696      */
21697     setRawValue : function(v){
21698         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21699     },
21700
21701     /**
21702      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21703      * @param {Mixed} value The value to set
21704      */
21705     setValue : function(v){
21706         this.value = v;
21707         if(this.rendered){
21708             this.el.dom.value = (v === null || v === undefined ? '' : v);
21709              this.validate();
21710         }
21711     },
21712
21713     adjustSize : function(w, h){
21714         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21715         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21716         return s;
21717     },
21718
21719     adjustWidth : function(tag, w){
21720         tag = tag.toLowerCase();
21721         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21722             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21723                 if(tag == 'input'){
21724                     return w + 2;
21725                 }
21726                 if(tag == 'textarea'){
21727                     return w-2;
21728                 }
21729             }else if(Roo.isOpera){
21730                 if(tag == 'input'){
21731                     return w + 2;
21732                 }
21733                 if(tag == 'textarea'){
21734                     return w-2;
21735                 }
21736             }
21737         }
21738         return w;
21739     }
21740 });
21741
21742
21743 // anything other than normal should be considered experimental
21744 Roo.form.Field.msgFx = {
21745     normal : {
21746         show: function(msgEl, f){
21747             msgEl.setDisplayed('block');
21748         },
21749
21750         hide : function(msgEl, f){
21751             msgEl.setDisplayed(false).update('');
21752         }
21753     },
21754
21755     slide : {
21756         show: function(msgEl, f){
21757             msgEl.slideIn('t', {stopFx:true});
21758         },
21759
21760         hide : function(msgEl, f){
21761             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21762         }
21763     },
21764
21765     slideRight : {
21766         show: function(msgEl, f){
21767             msgEl.fixDisplay();
21768             msgEl.alignTo(f.el, 'tl-tr');
21769             msgEl.slideIn('l', {stopFx:true});
21770         },
21771
21772         hide : function(msgEl, f){
21773             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21774         }
21775     }
21776 };/*
21777  * Based on:
21778  * Ext JS Library 1.1.1
21779  * Copyright(c) 2006-2007, Ext JS, LLC.
21780  *
21781  * Originally Released Under LGPL - original licence link has changed is not relivant.
21782  *
21783  * Fork - LGPL
21784  * <script type="text/javascript">
21785  */
21786  
21787
21788 /**
21789  * @class Roo.form.TextField
21790  * @extends Roo.form.Field
21791  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21792  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21793  * @constructor
21794  * Creates a new TextField
21795  * @param {Object} config Configuration options
21796  */
21797 Roo.form.TextField = function(config){
21798     Roo.form.TextField.superclass.constructor.call(this, config);
21799     this.addEvents({
21800         /**
21801          * @event autosize
21802          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21803          * according to the default logic, but this event provides a hook for the developer to apply additional
21804          * logic at runtime to resize the field if needed.
21805              * @param {Roo.form.Field} this This text field
21806              * @param {Number} width The new field width
21807              */
21808         autosize : true
21809     });
21810 };
21811
21812 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21813     /**
21814      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21815      */
21816     grow : false,
21817     /**
21818      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21819      */
21820     growMin : 30,
21821     /**
21822      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21823      */
21824     growMax : 800,
21825     /**
21826      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21827      */
21828     vtype : null,
21829     /**
21830      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21831      */
21832     maskRe : null,
21833     /**
21834      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21835      */
21836     disableKeyFilter : false,
21837     /**
21838      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21839      */
21840     allowBlank : true,
21841     /**
21842      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21843      */
21844     minLength : 0,
21845     /**
21846      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21847      */
21848     maxLength : Number.MAX_VALUE,
21849     /**
21850      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21851      */
21852     minLengthText : "The minimum length for this field is {0}",
21853     /**
21854      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21855      */
21856     maxLengthText : "The maximum length for this field is {0}",
21857     /**
21858      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21859      */
21860     selectOnFocus : false,
21861     /**
21862      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21863      */
21864     blankText : "This field is required",
21865     /**
21866      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21867      * If available, this function will be called only after the basic validators all return true, and will be passed the
21868      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21869      */
21870     validator : null,
21871     /**
21872      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21873      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21874      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21875      */
21876     regex : null,
21877     /**
21878      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21879      */
21880     regexText : "",
21881     /**
21882      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21883      */
21884     emptyText : null,
21885    
21886
21887     // private
21888     initEvents : function()
21889     {
21890         if (this.emptyText) {
21891             this.el.attr('placeholder', this.emptyText);
21892         }
21893         
21894         Roo.form.TextField.superclass.initEvents.call(this);
21895         if(this.validationEvent == 'keyup'){
21896             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21897             this.el.on('keyup', this.filterValidation, this);
21898         }
21899         else if(this.validationEvent !== false){
21900             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21901         }
21902         
21903         if(this.selectOnFocus){
21904             this.on("focus", this.preFocus, this);
21905             
21906         }
21907         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21908             this.el.on("keypress", this.filterKeys, this);
21909         }
21910         if(this.grow){
21911             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21912             this.el.on("click", this.autoSize,  this);
21913         }
21914         if(this.el.is('input[type=password]') && Roo.isSafari){
21915             this.el.on('keydown', this.SafariOnKeyDown, this);
21916         }
21917     },
21918
21919     processValue : function(value){
21920         if(this.stripCharsRe){
21921             var newValue = value.replace(this.stripCharsRe, '');
21922             if(newValue !== value){
21923                 this.setRawValue(newValue);
21924                 return newValue;
21925             }
21926         }
21927         return value;
21928     },
21929
21930     filterValidation : function(e){
21931         if(!e.isNavKeyPress()){
21932             this.validationTask.delay(this.validationDelay);
21933         }
21934     },
21935
21936     // private
21937     onKeyUp : function(e){
21938         if(!e.isNavKeyPress()){
21939             this.autoSize();
21940         }
21941     },
21942
21943     /**
21944      * Resets the current field value to the originally-loaded value and clears any validation messages.
21945      *  
21946      */
21947     reset : function(){
21948         Roo.form.TextField.superclass.reset.call(this);
21949        
21950     },
21951
21952     
21953     // private
21954     preFocus : function(){
21955         
21956         if(this.selectOnFocus){
21957             this.el.dom.select();
21958         }
21959     },
21960
21961     
21962     // private
21963     filterKeys : function(e){
21964         var k = e.getKey();
21965         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21966             return;
21967         }
21968         var c = e.getCharCode(), cc = String.fromCharCode(c);
21969         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21970             return;
21971         }
21972         if(!this.maskRe.test(cc)){
21973             e.stopEvent();
21974         }
21975     },
21976
21977     setValue : function(v){
21978         
21979         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21980         
21981         this.autoSize();
21982     },
21983
21984     /**
21985      * Validates a value according to the field's validation rules and marks the field as invalid
21986      * if the validation fails
21987      * @param {Mixed} value The value to validate
21988      * @return {Boolean} True if the value is valid, else false
21989      */
21990     validateValue : function(value){
21991         if(value.length < 1)  { // if it's blank
21992              if(this.allowBlank){
21993                 this.clearInvalid();
21994                 return true;
21995              }else{
21996                 this.markInvalid(this.blankText);
21997                 return false;
21998              }
21999         }
22000         if(value.length < this.minLength){
22001             this.markInvalid(String.format(this.minLengthText, this.minLength));
22002             return false;
22003         }
22004         if(value.length > this.maxLength){
22005             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
22006             return false;
22007         }
22008         if(this.vtype){
22009             var vt = Roo.form.VTypes;
22010             if(!vt[this.vtype](value, this)){
22011                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
22012                 return false;
22013             }
22014         }
22015         if(typeof this.validator == "function"){
22016             var msg = this.validator(value);
22017             if(msg !== true){
22018                 this.markInvalid(msg);
22019                 return false;
22020             }
22021         }
22022         if(this.regex && !this.regex.test(value)){
22023             this.markInvalid(this.regexText);
22024             return false;
22025         }
22026         return true;
22027     },
22028
22029     /**
22030      * Selects text in this field
22031      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
22032      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
22033      */
22034     selectText : function(start, end){
22035         var v = this.getRawValue();
22036         if(v.length > 0){
22037             start = start === undefined ? 0 : start;
22038             end = end === undefined ? v.length : end;
22039             var d = this.el.dom;
22040             if(d.setSelectionRange){
22041                 d.setSelectionRange(start, end);
22042             }else if(d.createTextRange){
22043                 var range = d.createTextRange();
22044                 range.moveStart("character", start);
22045                 range.moveEnd("character", v.length-end);
22046                 range.select();
22047             }
22048         }
22049     },
22050
22051     /**
22052      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
22053      * This only takes effect if grow = true, and fires the autosize event.
22054      */
22055     autoSize : function(){
22056         if(!this.grow || !this.rendered){
22057             return;
22058         }
22059         if(!this.metrics){
22060             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
22061         }
22062         var el = this.el;
22063         var v = el.dom.value;
22064         var d = document.createElement('div');
22065         d.appendChild(document.createTextNode(v));
22066         v = d.innerHTML;
22067         d = null;
22068         v += "&#160;";
22069         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
22070         this.el.setWidth(w);
22071         this.fireEvent("autosize", this, w);
22072     },
22073     
22074     // private
22075     SafariOnKeyDown : function(event)
22076     {
22077         // this is a workaround for a password hang bug on chrome/ webkit.
22078         
22079         var isSelectAll = false;
22080         
22081         if(this.el.dom.selectionEnd > 0){
22082             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
22083         }
22084         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
22085             event.preventDefault();
22086             this.setValue('');
22087             return;
22088         }
22089         
22090         if(isSelectAll){ // backspace and delete key
22091             
22092             event.preventDefault();
22093             // this is very hacky as keydown always get's upper case.
22094             //
22095             var cc = String.fromCharCode(event.getCharCode());
22096             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
22097             
22098         }
22099         
22100         
22101     }
22102 });/*
22103  * Based on:
22104  * Ext JS Library 1.1.1
22105  * Copyright(c) 2006-2007, Ext JS, LLC.
22106  *
22107  * Originally Released Under LGPL - original licence link has changed is not relivant.
22108  *
22109  * Fork - LGPL
22110  * <script type="text/javascript">
22111  */
22112  
22113 /**
22114  * @class Roo.form.Hidden
22115  * @extends Roo.form.TextField
22116  * Simple Hidden element used on forms 
22117  * 
22118  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
22119  * 
22120  * @constructor
22121  * Creates a new Hidden form element.
22122  * @param {Object} config Configuration options
22123  */
22124
22125
22126
22127 // easy hidden field...
22128 Roo.form.Hidden = function(config){
22129     Roo.form.Hidden.superclass.constructor.call(this, config);
22130 };
22131   
22132 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22133     fieldLabel:      '',
22134     inputType:      'hidden',
22135     width:          50,
22136     allowBlank:     true,
22137     labelSeparator: '',
22138     hidden:         true,
22139     itemCls :       'x-form-item-display-none'
22140
22141
22142 });
22143
22144
22145 /*
22146  * Based on:
22147  * Ext JS Library 1.1.1
22148  * Copyright(c) 2006-2007, Ext JS, LLC.
22149  *
22150  * Originally Released Under LGPL - original licence link has changed is not relivant.
22151  *
22152  * Fork - LGPL
22153  * <script type="text/javascript">
22154  */
22155  
22156 /**
22157  * @class Roo.form.TriggerField
22158  * @extends Roo.form.TextField
22159  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22160  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22161  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22162  * for which you can provide a custom implementation.  For example:
22163  * <pre><code>
22164 var trigger = new Roo.form.TriggerField();
22165 trigger.onTriggerClick = myTriggerFn;
22166 trigger.applyTo('my-field');
22167 </code></pre>
22168  *
22169  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22170  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22171  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22172  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22173  * @constructor
22174  * Create a new TriggerField.
22175  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22176  * to the base TextField)
22177  */
22178 Roo.form.TriggerField = function(config){
22179     this.mimicing = false;
22180     Roo.form.TriggerField.superclass.constructor.call(this, config);
22181 };
22182
22183 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22184     /**
22185      * @cfg {String} triggerClass A CSS class to apply to the trigger
22186      */
22187     /**
22188      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22189      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22190      */
22191     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22192     /**
22193      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22194      */
22195     hideTrigger:false,
22196
22197     /** @cfg {Boolean} grow @hide */
22198     /** @cfg {Number} growMin @hide */
22199     /** @cfg {Number} growMax @hide */
22200
22201     /**
22202      * @hide 
22203      * @method
22204      */
22205     autoSize: Roo.emptyFn,
22206     // private
22207     monitorTab : true,
22208     // private
22209     deferHeight : true,
22210
22211     
22212     actionMode : 'wrap',
22213     // private
22214     onResize : function(w, h){
22215         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22216         if(typeof w == 'number'){
22217             var x = w - this.trigger.getWidth();
22218             this.el.setWidth(this.adjustWidth('input', x));
22219             this.trigger.setStyle('left', x+'px');
22220         }
22221     },
22222
22223     // private
22224     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22225
22226     // private
22227     getResizeEl : function(){
22228         return this.wrap;
22229     },
22230
22231     // private
22232     getPositionEl : function(){
22233         return this.wrap;
22234     },
22235
22236     // private
22237     alignErrorIcon : function(){
22238         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22239     },
22240
22241     // private
22242     onRender : function(ct, position){
22243         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22244         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22245         this.trigger = this.wrap.createChild(this.triggerConfig ||
22246                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22247         if(this.hideTrigger){
22248             this.trigger.setDisplayed(false);
22249         }
22250         this.initTrigger();
22251         if(!this.width){
22252             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22253         }
22254     },
22255
22256     // private
22257     initTrigger : function(){
22258         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22259         this.trigger.addClassOnOver('x-form-trigger-over');
22260         this.trigger.addClassOnClick('x-form-trigger-click');
22261     },
22262
22263     // private
22264     onDestroy : function(){
22265         if(this.trigger){
22266             this.trigger.removeAllListeners();
22267             this.trigger.remove();
22268         }
22269         if(this.wrap){
22270             this.wrap.remove();
22271         }
22272         Roo.form.TriggerField.superclass.onDestroy.call(this);
22273     },
22274
22275     // private
22276     onFocus : function(){
22277         Roo.form.TriggerField.superclass.onFocus.call(this);
22278         if(!this.mimicing){
22279             this.wrap.addClass('x-trigger-wrap-focus');
22280             this.mimicing = true;
22281             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22282             if(this.monitorTab){
22283                 this.el.on("keydown", this.checkTab, this);
22284             }
22285         }
22286     },
22287
22288     // private
22289     checkTab : function(e){
22290         if(e.getKey() == e.TAB){
22291             this.triggerBlur();
22292         }
22293     },
22294
22295     // private
22296     onBlur : function(){
22297         // do nothing
22298     },
22299
22300     // private
22301     mimicBlur : function(e, t){
22302         if(!this.wrap.contains(t) && this.validateBlur()){
22303             this.triggerBlur();
22304         }
22305     },
22306
22307     // private
22308     triggerBlur : function(){
22309         this.mimicing = false;
22310         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22311         if(this.monitorTab){
22312             this.el.un("keydown", this.checkTab, this);
22313         }
22314         this.wrap.removeClass('x-trigger-wrap-focus');
22315         Roo.form.TriggerField.superclass.onBlur.call(this);
22316     },
22317
22318     // private
22319     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22320     validateBlur : function(e, t){
22321         return true;
22322     },
22323
22324     // private
22325     onDisable : function(){
22326         Roo.form.TriggerField.superclass.onDisable.call(this);
22327         if(this.wrap){
22328             this.wrap.addClass('x-item-disabled');
22329         }
22330     },
22331
22332     // private
22333     onEnable : function(){
22334         Roo.form.TriggerField.superclass.onEnable.call(this);
22335         if(this.wrap){
22336             this.wrap.removeClass('x-item-disabled');
22337         }
22338     },
22339
22340     // private
22341     onShow : function(){
22342         var ae = this.getActionEl();
22343         
22344         if(ae){
22345             ae.dom.style.display = '';
22346             ae.dom.style.visibility = 'visible';
22347         }
22348     },
22349
22350     // private
22351     
22352     onHide : function(){
22353         var ae = this.getActionEl();
22354         ae.dom.style.display = 'none';
22355     },
22356
22357     /**
22358      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22359      * by an implementing function.
22360      * @method
22361      * @param {EventObject} e
22362      */
22363     onTriggerClick : Roo.emptyFn
22364 });
22365
22366 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22367 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22368 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22369 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22370     initComponent : function(){
22371         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22372
22373         this.triggerConfig = {
22374             tag:'span', cls:'x-form-twin-triggers', cn:[
22375             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22376             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22377         ]};
22378     },
22379
22380     getTrigger : function(index){
22381         return this.triggers[index];
22382     },
22383
22384     initTrigger : function(){
22385         var ts = this.trigger.select('.x-form-trigger', true);
22386         this.wrap.setStyle('overflow', 'hidden');
22387         var triggerField = this;
22388         ts.each(function(t, all, index){
22389             t.hide = function(){
22390                 var w = triggerField.wrap.getWidth();
22391                 this.dom.style.display = 'none';
22392                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22393             };
22394             t.show = function(){
22395                 var w = triggerField.wrap.getWidth();
22396                 this.dom.style.display = '';
22397                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22398             };
22399             var triggerIndex = 'Trigger'+(index+1);
22400
22401             if(this['hide'+triggerIndex]){
22402                 t.dom.style.display = 'none';
22403             }
22404             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22405             t.addClassOnOver('x-form-trigger-over');
22406             t.addClassOnClick('x-form-trigger-click');
22407         }, this);
22408         this.triggers = ts.elements;
22409     },
22410
22411     onTrigger1Click : Roo.emptyFn,
22412     onTrigger2Click : Roo.emptyFn
22413 });/*
22414  * Based on:
22415  * Ext JS Library 1.1.1
22416  * Copyright(c) 2006-2007, Ext JS, LLC.
22417  *
22418  * Originally Released Under LGPL - original licence link has changed is not relivant.
22419  *
22420  * Fork - LGPL
22421  * <script type="text/javascript">
22422  */
22423  
22424 /**
22425  * @class Roo.form.TextArea
22426  * @extends Roo.form.TextField
22427  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22428  * support for auto-sizing.
22429  * @constructor
22430  * Creates a new TextArea
22431  * @param {Object} config Configuration options
22432  */
22433 Roo.form.TextArea = function(config){
22434     Roo.form.TextArea.superclass.constructor.call(this, config);
22435     // these are provided exchanges for backwards compat
22436     // minHeight/maxHeight were replaced by growMin/growMax to be
22437     // compatible with TextField growing config values
22438     if(this.minHeight !== undefined){
22439         this.growMin = this.minHeight;
22440     }
22441     if(this.maxHeight !== undefined){
22442         this.growMax = this.maxHeight;
22443     }
22444 };
22445
22446 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22447     /**
22448      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22449      */
22450     growMin : 60,
22451     /**
22452      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22453      */
22454     growMax: 1000,
22455     /**
22456      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22457      * in the field (equivalent to setting overflow: hidden, defaults to false)
22458      */
22459     preventScrollbars: false,
22460     /**
22461      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22462      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22463      */
22464
22465     // private
22466     onRender : function(ct, position){
22467         if(!this.el){
22468             this.defaultAutoCreate = {
22469                 tag: "textarea",
22470                 style:"width:300px;height:60px;",
22471                 autocomplete: "off"
22472             };
22473         }
22474         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22475         if(this.grow){
22476             this.textSizeEl = Roo.DomHelper.append(document.body, {
22477                 tag: "pre", cls: "x-form-grow-sizer"
22478             });
22479             if(this.preventScrollbars){
22480                 this.el.setStyle("overflow", "hidden");
22481             }
22482             this.el.setHeight(this.growMin);
22483         }
22484     },
22485
22486     onDestroy : function(){
22487         if(this.textSizeEl){
22488             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22489         }
22490         Roo.form.TextArea.superclass.onDestroy.call(this);
22491     },
22492
22493     // private
22494     onKeyUp : function(e){
22495         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22496             this.autoSize();
22497         }
22498     },
22499
22500     /**
22501      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22502      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22503      */
22504     autoSize : function(){
22505         if(!this.grow || !this.textSizeEl){
22506             return;
22507         }
22508         var el = this.el;
22509         var v = el.dom.value;
22510         var ts = this.textSizeEl;
22511
22512         ts.innerHTML = '';
22513         ts.appendChild(document.createTextNode(v));
22514         v = ts.innerHTML;
22515
22516         Roo.fly(ts).setWidth(this.el.getWidth());
22517         if(v.length < 1){
22518             v = "&#160;&#160;";
22519         }else{
22520             if(Roo.isIE){
22521                 v = v.replace(/\n/g, '<p>&#160;</p>');
22522             }
22523             v += "&#160;\n&#160;";
22524         }
22525         ts.innerHTML = v;
22526         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22527         if(h != this.lastHeight){
22528             this.lastHeight = h;
22529             this.el.setHeight(h);
22530             this.fireEvent("autosize", this, h);
22531         }
22532     }
22533 });/*
22534  * Based on:
22535  * Ext JS Library 1.1.1
22536  * Copyright(c) 2006-2007, Ext JS, LLC.
22537  *
22538  * Originally Released Under LGPL - original licence link has changed is not relivant.
22539  *
22540  * Fork - LGPL
22541  * <script type="text/javascript">
22542  */
22543  
22544
22545 /**
22546  * @class Roo.form.NumberField
22547  * @extends Roo.form.TextField
22548  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22549  * @constructor
22550  * Creates a new NumberField
22551  * @param {Object} config Configuration options
22552  */
22553 Roo.form.NumberField = function(config){
22554     Roo.form.NumberField.superclass.constructor.call(this, config);
22555 };
22556
22557 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22558     /**
22559      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22560      */
22561     fieldClass: "x-form-field x-form-num-field",
22562     /**
22563      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22564      */
22565     allowDecimals : true,
22566     /**
22567      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22568      */
22569     decimalSeparator : ".",
22570     /**
22571      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22572      */
22573     decimalPrecision : 2,
22574     /**
22575      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22576      */
22577     allowNegative : true,
22578     /**
22579      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22580      */
22581     minValue : Number.NEGATIVE_INFINITY,
22582     /**
22583      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22584      */
22585     maxValue : Number.MAX_VALUE,
22586     /**
22587      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22588      */
22589     minText : "The minimum value for this field is {0}",
22590     /**
22591      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22592      */
22593     maxText : "The maximum value for this field is {0}",
22594     /**
22595      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22596      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22597      */
22598     nanText : "{0} is not a valid number",
22599
22600     // private
22601     initEvents : function(){
22602         Roo.form.NumberField.superclass.initEvents.call(this);
22603         var allowed = "0123456789";
22604         if(this.allowDecimals){
22605             allowed += this.decimalSeparator;
22606         }
22607         if(this.allowNegative){
22608             allowed += "-";
22609         }
22610         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22611         var keyPress = function(e){
22612             var k = e.getKey();
22613             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22614                 return;
22615             }
22616             var c = e.getCharCode();
22617             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22618                 e.stopEvent();
22619             }
22620         };
22621         this.el.on("keypress", keyPress, this);
22622     },
22623
22624     // private
22625     validateValue : function(value){
22626         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22627             return false;
22628         }
22629         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22630              return true;
22631         }
22632         var num = this.parseValue(value);
22633         if(isNaN(num)){
22634             this.markInvalid(String.format(this.nanText, value));
22635             return false;
22636         }
22637         if(num < this.minValue){
22638             this.markInvalid(String.format(this.minText, this.minValue));
22639             return false;
22640         }
22641         if(num > this.maxValue){
22642             this.markInvalid(String.format(this.maxText, this.maxValue));
22643             return false;
22644         }
22645         return true;
22646     },
22647
22648     getValue : function(){
22649         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22650     },
22651
22652     // private
22653     parseValue : function(value){
22654         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22655         return isNaN(value) ? '' : value;
22656     },
22657
22658     // private
22659     fixPrecision : function(value){
22660         var nan = isNaN(value);
22661         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22662             return nan ? '' : value;
22663         }
22664         return parseFloat(value).toFixed(this.decimalPrecision);
22665     },
22666
22667     setValue : function(v){
22668         v = this.fixPrecision(v);
22669         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22670     },
22671
22672     // private
22673     decimalPrecisionFcn : function(v){
22674         return Math.floor(v);
22675     },
22676
22677     beforeBlur : function(){
22678         var v = this.parseValue(this.getRawValue());
22679         if(v){
22680             this.setValue(v);
22681         }
22682     }
22683 });/*
22684  * Based on:
22685  * Ext JS Library 1.1.1
22686  * Copyright(c) 2006-2007, Ext JS, LLC.
22687  *
22688  * Originally Released Under LGPL - original licence link has changed is not relivant.
22689  *
22690  * Fork - LGPL
22691  * <script type="text/javascript">
22692  */
22693  
22694 /**
22695  * @class Roo.form.DateField
22696  * @extends Roo.form.TriggerField
22697  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22698 * @constructor
22699 * Create a new DateField
22700 * @param {Object} config
22701  */
22702 Roo.form.DateField = function(config){
22703     Roo.form.DateField.superclass.constructor.call(this, config);
22704     
22705       this.addEvents({
22706          
22707         /**
22708          * @event select
22709          * Fires when a date is selected
22710              * @param {Roo.form.DateField} combo This combo box
22711              * @param {Date} date The date selected
22712              */
22713         'select' : true
22714          
22715     });
22716     
22717     
22718     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22719     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22720     this.ddMatch = null;
22721     if(this.disabledDates){
22722         var dd = this.disabledDates;
22723         var re = "(?:";
22724         for(var i = 0; i < dd.length; i++){
22725             re += dd[i];
22726             if(i != dd.length-1) re += "|";
22727         }
22728         this.ddMatch = new RegExp(re + ")");
22729     }
22730 };
22731
22732 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22733     /**
22734      * @cfg {String} format
22735      * The default date format string which can be overriden for localization support.  The format must be
22736      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22737      */
22738     format : "m/d/y",
22739     /**
22740      * @cfg {String} altFormats
22741      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22742      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22743      */
22744     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22745     /**
22746      * @cfg {Array} disabledDays
22747      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22748      */
22749     disabledDays : null,
22750     /**
22751      * @cfg {String} disabledDaysText
22752      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22753      */
22754     disabledDaysText : "Disabled",
22755     /**
22756      * @cfg {Array} disabledDates
22757      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22758      * expression so they are very powerful. Some examples:
22759      * <ul>
22760      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22761      * <li>["03/08", "09/16"] would disable those days for every year</li>
22762      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22763      * <li>["03/../2006"] would disable every day in March 2006</li>
22764      * <li>["^03"] would disable every day in every March</li>
22765      * </ul>
22766      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22767      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22768      */
22769     disabledDates : null,
22770     /**
22771      * @cfg {String} disabledDatesText
22772      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22773      */
22774     disabledDatesText : "Disabled",
22775     /**
22776      * @cfg {Date/String} minValue
22777      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22778      * valid format (defaults to null).
22779      */
22780     minValue : null,
22781     /**
22782      * @cfg {Date/String} maxValue
22783      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22784      * valid format (defaults to null).
22785      */
22786     maxValue : null,
22787     /**
22788      * @cfg {String} minText
22789      * The error text to display when the date in the cell is before minValue (defaults to
22790      * 'The date in this field must be after {minValue}').
22791      */
22792     minText : "The date in this field must be equal to or after {0}",
22793     /**
22794      * @cfg {String} maxText
22795      * The error text to display when the date in the cell is after maxValue (defaults to
22796      * 'The date in this field must be before {maxValue}').
22797      */
22798     maxText : "The date in this field must be equal to or before {0}",
22799     /**
22800      * @cfg {String} invalidText
22801      * The error text to display when the date in the field is invalid (defaults to
22802      * '{value} is not a valid date - it must be in the format {format}').
22803      */
22804     invalidText : "{0} is not a valid date - it must be in the format {1}",
22805     /**
22806      * @cfg {String} triggerClass
22807      * An additional CSS class used to style the trigger button.  The trigger will always get the
22808      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22809      * which displays a calendar icon).
22810      */
22811     triggerClass : 'x-form-date-trigger',
22812     
22813
22814     /**
22815      * @cfg {Boolean} useIso
22816      * if enabled, then the date field will use a hidden field to store the 
22817      * real value as iso formated date. default (false)
22818      */ 
22819     useIso : false,
22820     /**
22821      * @cfg {String/Object} autoCreate
22822      * A DomHelper element spec, or true for a default element spec (defaults to
22823      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22824      */ 
22825     // private
22826     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22827     
22828     // private
22829     hiddenField: false,
22830     
22831     onRender : function(ct, position)
22832     {
22833         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22834         if (this.useIso) {
22835             //this.el.dom.removeAttribute('name'); 
22836             Roo.log("Changing name?");
22837             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22838             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22839                     'before', true);
22840             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22841             // prevent input submission
22842             this.hiddenName = this.name;
22843         }
22844             
22845             
22846     },
22847     
22848     // private
22849     validateValue : function(value)
22850     {
22851         value = this.formatDate(value);
22852         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22853             Roo.log('super failed');
22854             return false;
22855         }
22856         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22857              return true;
22858         }
22859         var svalue = value;
22860         value = this.parseDate(value);
22861         if(!value){
22862             Roo.log('parse date failed' + svalue);
22863             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22864             return false;
22865         }
22866         var time = value.getTime();
22867         if(this.minValue && time < this.minValue.getTime()){
22868             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22869             return false;
22870         }
22871         if(this.maxValue && time > this.maxValue.getTime()){
22872             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22873             return false;
22874         }
22875         if(this.disabledDays){
22876             var day = value.getDay();
22877             for(var i = 0; i < this.disabledDays.length; i++) {
22878                 if(day === this.disabledDays[i]){
22879                     this.markInvalid(this.disabledDaysText);
22880                     return false;
22881                 }
22882             }
22883         }
22884         var fvalue = this.formatDate(value);
22885         if(this.ddMatch && this.ddMatch.test(fvalue)){
22886             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22887             return false;
22888         }
22889         return true;
22890     },
22891
22892     // private
22893     // Provides logic to override the default TriggerField.validateBlur which just returns true
22894     validateBlur : function(){
22895         return !this.menu || !this.menu.isVisible();
22896     },
22897     
22898     getName: function()
22899     {
22900         // returns hidden if it's set..
22901         if (!this.rendered) {return ''};
22902         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22903         
22904     },
22905
22906     /**
22907      * Returns the current date value of the date field.
22908      * @return {Date} The date value
22909      */
22910     getValue : function(){
22911         
22912         return  this.hiddenField ?
22913                 this.hiddenField.value :
22914                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22915     },
22916
22917     /**
22918      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22919      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22920      * (the default format used is "m/d/y").
22921      * <br />Usage:
22922      * <pre><code>
22923 //All of these calls set the same date value (May 4, 2006)
22924
22925 //Pass a date object:
22926 var dt = new Date('5/4/06');
22927 dateField.setValue(dt);
22928
22929 //Pass a date string (default format):
22930 dateField.setValue('5/4/06');
22931
22932 //Pass a date string (custom format):
22933 dateField.format = 'Y-m-d';
22934 dateField.setValue('2006-5-4');
22935 </code></pre>
22936      * @param {String/Date} date The date or valid date string
22937      */
22938     setValue : function(date){
22939         if (this.hiddenField) {
22940             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22941         }
22942         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22943         // make sure the value field is always stored as a date..
22944         this.value = this.parseDate(date);
22945         
22946         
22947     },
22948
22949     // private
22950     parseDate : function(value){
22951         if(!value || value instanceof Date){
22952             return value;
22953         }
22954         var v = Date.parseDate(value, this.format);
22955          if (!v && this.useIso) {
22956             v = Date.parseDate(value, 'Y-m-d');
22957         }
22958         if(!v && this.altFormats){
22959             if(!this.altFormatsArray){
22960                 this.altFormatsArray = this.altFormats.split("|");
22961             }
22962             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22963                 v = Date.parseDate(value, this.altFormatsArray[i]);
22964             }
22965         }
22966         return v;
22967     },
22968
22969     // private
22970     formatDate : function(date, fmt){
22971         return (!date || !(date instanceof Date)) ?
22972                date : date.dateFormat(fmt || this.format);
22973     },
22974
22975     // private
22976     menuListeners : {
22977         select: function(m, d){
22978             
22979             this.setValue(d);
22980             this.fireEvent('select', this, d);
22981         },
22982         show : function(){ // retain focus styling
22983             this.onFocus();
22984         },
22985         hide : function(){
22986             this.focus.defer(10, this);
22987             var ml = this.menuListeners;
22988             this.menu.un("select", ml.select,  this);
22989             this.menu.un("show", ml.show,  this);
22990             this.menu.un("hide", ml.hide,  this);
22991         }
22992     },
22993
22994     // private
22995     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22996     onTriggerClick : function(){
22997         if(this.disabled){
22998             return;
22999         }
23000         if(this.menu == null){
23001             this.menu = new Roo.menu.DateMenu();
23002         }
23003         Roo.apply(this.menu.picker,  {
23004             showClear: this.allowBlank,
23005             minDate : this.minValue,
23006             maxDate : this.maxValue,
23007             disabledDatesRE : this.ddMatch,
23008             disabledDatesText : this.disabledDatesText,
23009             disabledDays : this.disabledDays,
23010             disabledDaysText : this.disabledDaysText,
23011             format : this.useIso ? 'Y-m-d' : this.format,
23012             minText : String.format(this.minText, this.formatDate(this.minValue)),
23013             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23014         });
23015         this.menu.on(Roo.apply({}, this.menuListeners, {
23016             scope:this
23017         }));
23018         this.menu.picker.setValue(this.getValue() || new Date());
23019         this.menu.show(this.el, "tl-bl?");
23020     },
23021
23022     beforeBlur : function(){
23023         var v = this.parseDate(this.getRawValue());
23024         if(v){
23025             this.setValue(v);
23026         }
23027     }
23028
23029     /** @cfg {Boolean} grow @hide */
23030     /** @cfg {Number} growMin @hide */
23031     /** @cfg {Number} growMax @hide */
23032     /**
23033      * @hide
23034      * @method autoSize
23035      */
23036 });/*
23037  * Based on:
23038  * Ext JS Library 1.1.1
23039  * Copyright(c) 2006-2007, Ext JS, LLC.
23040  *
23041  * Originally Released Under LGPL - original licence link has changed is not relivant.
23042  *
23043  * Fork - LGPL
23044  * <script type="text/javascript">
23045  */
23046  
23047 /**
23048  * @class Roo.form.MonthField
23049  * @extends Roo.form.TriggerField
23050  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
23051 * @constructor
23052 * Create a new MonthField
23053 * @param {Object} config
23054  */
23055 Roo.form.MonthField = function(config){
23056     
23057     Roo.form.MonthField.superclass.constructor.call(this, config);
23058     
23059       this.addEvents({
23060          
23061         /**
23062          * @event select
23063          * Fires when a date is selected
23064              * @param {Roo.form.MonthFieeld} combo This combo box
23065              * @param {Date} date The date selected
23066              */
23067         'select' : true
23068          
23069     });
23070     
23071     
23072     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
23073     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
23074     this.ddMatch = null;
23075     if(this.disabledDates){
23076         var dd = this.disabledDates;
23077         var re = "(?:";
23078         for(var i = 0; i < dd.length; i++){
23079             re += dd[i];
23080             if(i != dd.length-1) re += "|";
23081         }
23082         this.ddMatch = new RegExp(re + ")");
23083     }
23084 };
23085
23086 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
23087     /**
23088      * @cfg {String} format
23089      * The default date format string which can be overriden for localization support.  The format must be
23090      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
23091      */
23092     format : "M Y",
23093     /**
23094      * @cfg {String} altFormats
23095      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
23096      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
23097      */
23098     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
23099     /**
23100      * @cfg {Array} disabledDays
23101      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
23102      */
23103     disabledDays : [0,1,2,3,4,5,6],
23104     /**
23105      * @cfg {String} disabledDaysText
23106      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
23107      */
23108     disabledDaysText : "Disabled",
23109     /**
23110      * @cfg {Array} disabledDates
23111      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
23112      * expression so they are very powerful. Some examples:
23113      * <ul>
23114      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
23115      * <li>["03/08", "09/16"] would disable those days for every year</li>
23116      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
23117      * <li>["03/../2006"] would disable every day in March 2006</li>
23118      * <li>["^03"] would disable every day in every March</li>
23119      * </ul>
23120      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
23121      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
23122      */
23123     disabledDates : null,
23124     /**
23125      * @cfg {String} disabledDatesText
23126      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
23127      */
23128     disabledDatesText : "Disabled",
23129     /**
23130      * @cfg {Date/String} minValue
23131      * The minimum allowed date. Can be either a Javascript date object or a string date in a
23132      * valid format (defaults to null).
23133      */
23134     minValue : null,
23135     /**
23136      * @cfg {Date/String} maxValue
23137      * The maximum allowed date. Can be either a Javascript date object or a string date in a
23138      * valid format (defaults to null).
23139      */
23140     maxValue : null,
23141     /**
23142      * @cfg {String} minText
23143      * The error text to display when the date in the cell is before minValue (defaults to
23144      * 'The date in this field must be after {minValue}').
23145      */
23146     minText : "The date in this field must be equal to or after {0}",
23147     /**
23148      * @cfg {String} maxTextf
23149      * The error text to display when the date in the cell is after maxValue (defaults to
23150      * 'The date in this field must be before {maxValue}').
23151      */
23152     maxText : "The date in this field must be equal to or before {0}",
23153     /**
23154      * @cfg {String} invalidText
23155      * The error text to display when the date in the field is invalid (defaults to
23156      * '{value} is not a valid date - it must be in the format {format}').
23157      */
23158     invalidText : "{0} is not a valid date - it must be in the format {1}",
23159     /**
23160      * @cfg {String} triggerClass
23161      * An additional CSS class used to style the trigger button.  The trigger will always get the
23162      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23163      * which displays a calendar icon).
23164      */
23165     triggerClass : 'x-form-date-trigger',
23166     
23167
23168     /**
23169      * @cfg {Boolean} useIso
23170      * if enabled, then the date field will use a hidden field to store the 
23171      * real value as iso formated date. default (true)
23172      */ 
23173     useIso : true,
23174     /**
23175      * @cfg {String/Object} autoCreate
23176      * A DomHelper element spec, or true for a default element spec (defaults to
23177      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23178      */ 
23179     // private
23180     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23181     
23182     // private
23183     hiddenField: false,
23184     
23185     hideMonthPicker : false,
23186     
23187     onRender : function(ct, position)
23188     {
23189         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23190         if (this.useIso) {
23191             this.el.dom.removeAttribute('name'); 
23192             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23193                     'before', true);
23194             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23195             // prevent input submission
23196             this.hiddenName = this.name;
23197         }
23198             
23199             
23200     },
23201     
23202     // private
23203     validateValue : function(value)
23204     {
23205         value = this.formatDate(value);
23206         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
23207             return false;
23208         }
23209         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23210              return true;
23211         }
23212         var svalue = value;
23213         value = this.parseDate(value);
23214         if(!value){
23215             this.markInvalid(String.format(this.invalidText, svalue, this.format));
23216             return false;
23217         }
23218         var time = value.getTime();
23219         if(this.minValue && time < this.minValue.getTime()){
23220             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23221             return false;
23222         }
23223         if(this.maxValue && time > this.maxValue.getTime()){
23224             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23225             return false;
23226         }
23227         /*if(this.disabledDays){
23228             var day = value.getDay();
23229             for(var i = 0; i < this.disabledDays.length; i++) {
23230                 if(day === this.disabledDays[i]){
23231                     this.markInvalid(this.disabledDaysText);
23232                     return false;
23233                 }
23234             }
23235         }
23236         */
23237         var fvalue = this.formatDate(value);
23238         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23239             this.markInvalid(String.format(this.disabledDatesText, fvalue));
23240             return false;
23241         }
23242         */
23243         return true;
23244     },
23245
23246     // private
23247     // Provides logic to override the default TriggerField.validateBlur which just returns true
23248     validateBlur : function(){
23249         return !this.menu || !this.menu.isVisible();
23250     },
23251
23252     /**
23253      * Returns the current date value of the date field.
23254      * @return {Date} The date value
23255      */
23256     getValue : function(){
23257         
23258         
23259         
23260         return  this.hiddenField ?
23261                 this.hiddenField.value :
23262                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
23263     },
23264
23265     /**
23266      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
23267      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
23268      * (the default format used is "m/d/y").
23269      * <br />Usage:
23270      * <pre><code>
23271 //All of these calls set the same date value (May 4, 2006)
23272
23273 //Pass a date object:
23274 var dt = new Date('5/4/06');
23275 monthField.setValue(dt);
23276
23277 //Pass a date string (default format):
23278 monthField.setValue('5/4/06');
23279
23280 //Pass a date string (custom format):
23281 monthField.format = 'Y-m-d';
23282 monthField.setValue('2006-5-4');
23283 </code></pre>
23284      * @param {String/Date} date The date or valid date string
23285      */
23286     setValue : function(date){
23287         Roo.log('month setValue' + date);
23288         // can only be first of month..
23289         
23290         var val = this.parseDate(date);
23291         
23292         if (this.hiddenField) {
23293             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23294         }
23295         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23296         this.value = this.parseDate(date);
23297     },
23298
23299     // private
23300     parseDate : function(value){
23301         if(!value || value instanceof Date){
23302             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
23303             return value;
23304         }
23305         var v = Date.parseDate(value, this.format);
23306         if (!v && this.useIso) {
23307             v = Date.parseDate(value, 'Y-m-d');
23308         }
23309         if (v) {
23310             // 
23311             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
23312         }
23313         
23314         
23315         if(!v && this.altFormats){
23316             if(!this.altFormatsArray){
23317                 this.altFormatsArray = this.altFormats.split("|");
23318             }
23319             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23320                 v = Date.parseDate(value, this.altFormatsArray[i]);
23321             }
23322         }
23323         return v;
23324     },
23325
23326     // private
23327     formatDate : function(date, fmt){
23328         return (!date || !(date instanceof Date)) ?
23329                date : date.dateFormat(fmt || this.format);
23330     },
23331
23332     // private
23333     menuListeners : {
23334         select: function(m, d){
23335             this.setValue(d);
23336             this.fireEvent('select', this, d);
23337         },
23338         show : function(){ // retain focus styling
23339             this.onFocus();
23340         },
23341         hide : function(){
23342             this.focus.defer(10, this);
23343             var ml = this.menuListeners;
23344             this.menu.un("select", ml.select,  this);
23345             this.menu.un("show", ml.show,  this);
23346             this.menu.un("hide", ml.hide,  this);
23347         }
23348     },
23349     // private
23350     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23351     onTriggerClick : function(){
23352         if(this.disabled){
23353             return;
23354         }
23355         if(this.menu == null){
23356             this.menu = new Roo.menu.DateMenu();
23357            
23358         }
23359         
23360         Roo.apply(this.menu.picker,  {
23361             
23362             showClear: this.allowBlank,
23363             minDate : this.minValue,
23364             maxDate : this.maxValue,
23365             disabledDatesRE : this.ddMatch,
23366             disabledDatesText : this.disabledDatesText,
23367             
23368             format : this.useIso ? 'Y-m-d' : this.format,
23369             minText : String.format(this.minText, this.formatDate(this.minValue)),
23370             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23371             
23372         });
23373          this.menu.on(Roo.apply({}, this.menuListeners, {
23374             scope:this
23375         }));
23376        
23377         
23378         var m = this.menu;
23379         var p = m.picker;
23380         
23381         // hide month picker get's called when we called by 'before hide';
23382         
23383         var ignorehide = true;
23384         p.hideMonthPicker  = function(disableAnim){
23385             if (ignorehide) {
23386                 return;
23387             }
23388              if(this.monthPicker){
23389                 Roo.log("hideMonthPicker called");
23390                 if(disableAnim === true){
23391                     this.monthPicker.hide();
23392                 }else{
23393                     this.monthPicker.slideOut('t', {duration:.2});
23394                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
23395                     p.fireEvent("select", this, this.value);
23396                     m.hide();
23397                 }
23398             }
23399         }
23400         
23401         Roo.log('picker set value');
23402         Roo.log(this.getValue());
23403         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
23404         m.show(this.el, 'tl-bl?');
23405         ignorehide  = false;
23406         // this will trigger hideMonthPicker..
23407         
23408         
23409         // hidden the day picker
23410         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23411         
23412         
23413         
23414       
23415         
23416         p.showMonthPicker.defer(100, p);
23417     
23418         
23419        
23420     },
23421
23422     beforeBlur : function(){
23423         var v = this.parseDate(this.getRawValue());
23424         if(v){
23425             this.setValue(v);
23426         }
23427     }
23428
23429     /** @cfg {Boolean} grow @hide */
23430     /** @cfg {Number} growMin @hide */
23431     /** @cfg {Number} growMax @hide */
23432     /**
23433      * @hide
23434      * @method autoSize
23435      */
23436 });/*
23437  * Based on:
23438  * Ext JS Library 1.1.1
23439  * Copyright(c) 2006-2007, Ext JS, LLC.
23440  *
23441  * Originally Released Under LGPL - original licence link has changed is not relivant.
23442  *
23443  * Fork - LGPL
23444  * <script type="text/javascript">
23445  */
23446  
23447
23448 /**
23449  * @class Roo.form.ComboBox
23450  * @extends Roo.form.TriggerField
23451  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23452  * @constructor
23453  * Create a new ComboBox.
23454  * @param {Object} config Configuration options
23455  */
23456 Roo.form.ComboBox = function(config){
23457     Roo.form.ComboBox.superclass.constructor.call(this, config);
23458     this.addEvents({
23459         /**
23460          * @event expand
23461          * Fires when the dropdown list is expanded
23462              * @param {Roo.form.ComboBox} combo This combo box
23463              */
23464         'expand' : true,
23465         /**
23466          * @event collapse
23467          * Fires when the dropdown list is collapsed
23468              * @param {Roo.form.ComboBox} combo This combo box
23469              */
23470         'collapse' : true,
23471         /**
23472          * @event beforeselect
23473          * Fires before a list item is selected. Return false to cancel the selection.
23474              * @param {Roo.form.ComboBox} combo This combo box
23475              * @param {Roo.data.Record} record The data record returned from the underlying store
23476              * @param {Number} index The index of the selected item in the dropdown list
23477              */
23478         'beforeselect' : true,
23479         /**
23480          * @event select
23481          * Fires when a list item is selected
23482              * @param {Roo.form.ComboBox} combo This combo box
23483              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23484              * @param {Number} index The index of the selected item in the dropdown list
23485              */
23486         'select' : true,
23487         /**
23488          * @event beforequery
23489          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23490          * The event object passed has these properties:
23491              * @param {Roo.form.ComboBox} combo This combo box
23492              * @param {String} query The query
23493              * @param {Boolean} forceAll true to force "all" query
23494              * @param {Boolean} cancel true to cancel the query
23495              * @param {Object} e The query event object
23496              */
23497         'beforequery': true,
23498          /**
23499          * @event add
23500          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23501              * @param {Roo.form.ComboBox} combo This combo box
23502              */
23503         'add' : true,
23504         /**
23505          * @event edit
23506          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23507              * @param {Roo.form.ComboBox} combo This combo box
23508              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23509              */
23510         'edit' : true
23511         
23512         
23513     });
23514     if(this.transform){
23515         this.allowDomMove = false;
23516         var s = Roo.getDom(this.transform);
23517         if(!this.hiddenName){
23518             this.hiddenName = s.name;
23519         }
23520         if(!this.store){
23521             this.mode = 'local';
23522             var d = [], opts = s.options;
23523             for(var i = 0, len = opts.length;i < len; i++){
23524                 var o = opts[i];
23525                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23526                 if(o.selected) {
23527                     this.value = value;
23528                 }
23529                 d.push([value, o.text]);
23530             }
23531             this.store = new Roo.data.SimpleStore({
23532                 'id': 0,
23533                 fields: ['value', 'text'],
23534                 data : d
23535             });
23536             this.valueField = 'value';
23537             this.displayField = 'text';
23538         }
23539         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23540         if(!this.lazyRender){
23541             this.target = true;
23542             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23543             s.parentNode.removeChild(s); // remove it
23544             this.render(this.el.parentNode);
23545         }else{
23546             s.parentNode.removeChild(s); // remove it
23547         }
23548
23549     }
23550     if (this.store) {
23551         this.store = Roo.factory(this.store, Roo.data);
23552     }
23553     
23554     this.selectedIndex = -1;
23555     if(this.mode == 'local'){
23556         if(config.queryDelay === undefined){
23557             this.queryDelay = 10;
23558         }
23559         if(config.minChars === undefined){
23560             this.minChars = 0;
23561         }
23562     }
23563 };
23564
23565 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23566     /**
23567      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23568      */
23569     /**
23570      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23571      * rendering into an Roo.Editor, defaults to false)
23572      */
23573     /**
23574      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23575      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23576      */
23577     /**
23578      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23579      */
23580     /**
23581      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23582      * the dropdown list (defaults to undefined, with no header element)
23583      */
23584
23585      /**
23586      * @cfg {String/Roo.Template} tpl The template to use to render the output
23587      */
23588      
23589     // private
23590     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23591     /**
23592      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23593      */
23594     listWidth: undefined,
23595     /**
23596      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23597      * mode = 'remote' or 'text' if mode = 'local')
23598      */
23599     displayField: undefined,
23600     /**
23601      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23602      * mode = 'remote' or 'value' if mode = 'local'). 
23603      * Note: use of a valueField requires the user make a selection
23604      * in order for a value to be mapped.
23605      */
23606     valueField: undefined,
23607     
23608     
23609     /**
23610      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23611      * field's data value (defaults to the underlying DOM element's name)
23612      */
23613     hiddenName: undefined,
23614     /**
23615      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23616      */
23617     listClass: '',
23618     /**
23619      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23620      */
23621     selectedClass: 'x-combo-selected',
23622     /**
23623      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23624      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23625      * which displays a downward arrow icon).
23626      */
23627     triggerClass : 'x-form-arrow-trigger',
23628     /**
23629      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23630      */
23631     shadow:'sides',
23632     /**
23633      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23634      * anchor positions (defaults to 'tl-bl')
23635      */
23636     listAlign: 'tl-bl?',
23637     /**
23638      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23639      */
23640     maxHeight: 300,
23641     /**
23642      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23643      * query specified by the allQuery config option (defaults to 'query')
23644      */
23645     triggerAction: 'query',
23646     /**
23647      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23648      * (defaults to 4, does not apply if editable = false)
23649      */
23650     minChars : 4,
23651     /**
23652      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23653      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23654      */
23655     typeAhead: false,
23656     /**
23657      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23658      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23659      */
23660     queryDelay: 500,
23661     /**
23662      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23663      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23664      */
23665     pageSize: 0,
23666     /**
23667      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23668      * when editable = true (defaults to false)
23669      */
23670     selectOnFocus:false,
23671     /**
23672      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23673      */
23674     queryParam: 'query',
23675     /**
23676      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23677      * when mode = 'remote' (defaults to 'Loading...')
23678      */
23679     loadingText: 'Loading...',
23680     /**
23681      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23682      */
23683     resizable: false,
23684     /**
23685      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23686      */
23687     handleHeight : 8,
23688     /**
23689      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23690      * traditional select (defaults to true)
23691      */
23692     editable: true,
23693     /**
23694      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23695      */
23696     allQuery: '',
23697     /**
23698      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23699      */
23700     mode: 'remote',
23701     /**
23702      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23703      * listWidth has a higher value)
23704      */
23705     minListWidth : 70,
23706     /**
23707      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23708      * allow the user to set arbitrary text into the field (defaults to false)
23709      */
23710     forceSelection:false,
23711     /**
23712      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23713      * if typeAhead = true (defaults to 250)
23714      */
23715     typeAheadDelay : 250,
23716     /**
23717      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23718      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23719      */
23720     valueNotFoundText : undefined,
23721     /**
23722      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23723      */
23724     blockFocus : false,
23725     
23726     /**
23727      * @cfg {Boolean} disableClear Disable showing of clear button.
23728      */
23729     disableClear : false,
23730     /**
23731      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23732      */
23733     alwaysQuery : false,
23734     
23735     //private
23736     addicon : false,
23737     editicon: false,
23738     
23739     // element that contains real text value.. (when hidden is used..)
23740      
23741     // private
23742     onRender : function(ct, position){
23743         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23744         if(this.hiddenName){
23745             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23746                     'before', true);
23747             this.hiddenField.value =
23748                 this.hiddenValue !== undefined ? this.hiddenValue :
23749                 this.value !== undefined ? this.value : '';
23750
23751             // prevent input submission
23752             this.el.dom.removeAttribute('name');
23753              
23754              
23755         }
23756         if(Roo.isGecko){
23757             this.el.dom.setAttribute('autocomplete', 'off');
23758         }
23759
23760         var cls = 'x-combo-list';
23761
23762         this.list = new Roo.Layer({
23763             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23764         });
23765
23766         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23767         this.list.setWidth(lw);
23768         this.list.swallowEvent('mousewheel');
23769         this.assetHeight = 0;
23770
23771         if(this.title){
23772             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23773             this.assetHeight += this.header.getHeight();
23774         }
23775
23776         this.innerList = this.list.createChild({cls:cls+'-inner'});
23777         this.innerList.on('mouseover', this.onViewOver, this);
23778         this.innerList.on('mousemove', this.onViewMove, this);
23779         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23780         
23781         if(this.allowBlank && !this.pageSize && !this.disableClear){
23782             this.footer = this.list.createChild({cls:cls+'-ft'});
23783             this.pageTb = new Roo.Toolbar(this.footer);
23784            
23785         }
23786         if(this.pageSize){
23787             this.footer = this.list.createChild({cls:cls+'-ft'});
23788             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23789                     {pageSize: this.pageSize});
23790             
23791         }
23792         
23793         if (this.pageTb && this.allowBlank && !this.disableClear) {
23794             var _this = this;
23795             this.pageTb.add(new Roo.Toolbar.Fill(), {
23796                 cls: 'x-btn-icon x-btn-clear',
23797                 text: '&#160;',
23798                 handler: function()
23799                 {
23800                     _this.collapse();
23801                     _this.clearValue();
23802                     _this.onSelect(false, -1);
23803                 }
23804             });
23805         }
23806         if (this.footer) {
23807             this.assetHeight += this.footer.getHeight();
23808         }
23809         
23810
23811         if(!this.tpl){
23812             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23813         }
23814
23815         this.view = new Roo.View(this.innerList, this.tpl, {
23816             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23817         });
23818
23819         this.view.on('click', this.onViewClick, this);
23820
23821         this.store.on('beforeload', this.onBeforeLoad, this);
23822         this.store.on('load', this.onLoad, this);
23823         this.store.on('loadexception', this.onLoadException, this);
23824
23825         if(this.resizable){
23826             this.resizer = new Roo.Resizable(this.list,  {
23827                pinned:true, handles:'se'
23828             });
23829             this.resizer.on('resize', function(r, w, h){
23830                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23831                 this.listWidth = w;
23832                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23833                 this.restrictHeight();
23834             }, this);
23835             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23836         }
23837         if(!this.editable){
23838             this.editable = true;
23839             this.setEditable(false);
23840         }  
23841         
23842         
23843         if (typeof(this.events.add.listeners) != 'undefined') {
23844             
23845             this.addicon = this.wrap.createChild(
23846                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23847        
23848             this.addicon.on('click', function(e) {
23849                 this.fireEvent('add', this);
23850             }, this);
23851         }
23852         if (typeof(this.events.edit.listeners) != 'undefined') {
23853             
23854             this.editicon = this.wrap.createChild(
23855                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23856             if (this.addicon) {
23857                 this.editicon.setStyle('margin-left', '40px');
23858             }
23859             this.editicon.on('click', function(e) {
23860                 
23861                 // we fire even  if inothing is selected..
23862                 this.fireEvent('edit', this, this.lastData );
23863                 
23864             }, this);
23865         }
23866         
23867         
23868         
23869     },
23870
23871     // private
23872     initEvents : function(){
23873         Roo.form.ComboBox.superclass.initEvents.call(this);
23874
23875         this.keyNav = new Roo.KeyNav(this.el, {
23876             "up" : function(e){
23877                 this.inKeyMode = true;
23878                 this.selectPrev();
23879             },
23880
23881             "down" : function(e){
23882                 if(!this.isExpanded()){
23883                     this.onTriggerClick();
23884                 }else{
23885                     this.inKeyMode = true;
23886                     this.selectNext();
23887                 }
23888             },
23889
23890             "enter" : function(e){
23891                 this.onViewClick();
23892                 //return true;
23893             },
23894
23895             "esc" : function(e){
23896                 this.collapse();
23897             },
23898
23899             "tab" : function(e){
23900                 this.onViewClick(false);
23901                 this.fireEvent("specialkey", this, e);
23902                 return true;
23903             },
23904
23905             scope : this,
23906
23907             doRelay : function(foo, bar, hname){
23908                 if(hname == 'down' || this.scope.isExpanded()){
23909                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23910                 }
23911                 return true;
23912             },
23913
23914             forceKeyDown: true
23915         });
23916         this.queryDelay = Math.max(this.queryDelay || 10,
23917                 this.mode == 'local' ? 10 : 250);
23918         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23919         if(this.typeAhead){
23920             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23921         }
23922         if(this.editable !== false){
23923             this.el.on("keyup", this.onKeyUp, this);
23924         }
23925         if(this.forceSelection){
23926             this.on('blur', this.doForce, this);
23927         }
23928     },
23929
23930     onDestroy : function(){
23931         if(this.view){
23932             this.view.setStore(null);
23933             this.view.el.removeAllListeners();
23934             this.view.el.remove();
23935             this.view.purgeListeners();
23936         }
23937         if(this.list){
23938             this.list.destroy();
23939         }
23940         if(this.store){
23941             this.store.un('beforeload', this.onBeforeLoad, this);
23942             this.store.un('load', this.onLoad, this);
23943             this.store.un('loadexception', this.onLoadException, this);
23944         }
23945         Roo.form.ComboBox.superclass.onDestroy.call(this);
23946     },
23947
23948     // private
23949     fireKey : function(e){
23950         if(e.isNavKeyPress() && !this.list.isVisible()){
23951             this.fireEvent("specialkey", this, e);
23952         }
23953     },
23954
23955     // private
23956     onResize: function(w, h){
23957         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23958         
23959         if(typeof w != 'number'){
23960             // we do not handle it!?!?
23961             return;
23962         }
23963         var tw = this.trigger.getWidth();
23964         tw += this.addicon ? this.addicon.getWidth() : 0;
23965         tw += this.editicon ? this.editicon.getWidth() : 0;
23966         var x = w - tw;
23967         this.el.setWidth( this.adjustWidth('input', x));
23968             
23969         this.trigger.setStyle('left', x+'px');
23970         
23971         if(this.list && this.listWidth === undefined){
23972             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23973             this.list.setWidth(lw);
23974             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23975         }
23976         
23977     
23978         
23979     },
23980
23981     /**
23982      * Allow or prevent the user from directly editing the field text.  If false is passed,
23983      * the user will only be able to select from the items defined in the dropdown list.  This method
23984      * is the runtime equivalent of setting the 'editable' config option at config time.
23985      * @param {Boolean} value True to allow the user to directly edit the field text
23986      */
23987     setEditable : function(value){
23988         if(value == this.editable){
23989             return;
23990         }
23991         this.editable = value;
23992         if(!value){
23993             this.el.dom.setAttribute('readOnly', true);
23994             this.el.on('mousedown', this.onTriggerClick,  this);
23995             this.el.addClass('x-combo-noedit');
23996         }else{
23997             this.el.dom.setAttribute('readOnly', false);
23998             this.el.un('mousedown', this.onTriggerClick,  this);
23999             this.el.removeClass('x-combo-noedit');
24000         }
24001     },
24002
24003     // private
24004     onBeforeLoad : function(){
24005         if(!this.hasFocus){
24006             return;
24007         }
24008         this.innerList.update(this.loadingText ?
24009                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
24010         this.restrictHeight();
24011         this.selectedIndex = -1;
24012     },
24013
24014     // private
24015     onLoad : function(){
24016         if(!this.hasFocus){
24017             return;
24018         }
24019         if(this.store.getCount() > 0){
24020             this.expand();
24021             this.restrictHeight();
24022             if(this.lastQuery == this.allQuery){
24023                 if(this.editable){
24024                     this.el.dom.select();
24025                 }
24026                 if(!this.selectByValue(this.value, true)){
24027                     this.select(0, true);
24028                 }
24029             }else{
24030                 this.selectNext();
24031                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
24032                     this.taTask.delay(this.typeAheadDelay);
24033                 }
24034             }
24035         }else{
24036             this.onEmptyResults();
24037         }
24038         //this.el.focus();
24039     },
24040     // private
24041     onLoadException : function()
24042     {
24043         this.collapse();
24044         Roo.log(this.store.reader.jsonData);
24045         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
24046             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
24047         }
24048         
24049         
24050     },
24051     // private
24052     onTypeAhead : function(){
24053         if(this.store.getCount() > 0){
24054             var r = this.store.getAt(0);
24055             var newValue = r.data[this.displayField];
24056             var len = newValue.length;
24057             var selStart = this.getRawValue().length;
24058             if(selStart != len){
24059                 this.setRawValue(newValue);
24060                 this.selectText(selStart, newValue.length);
24061             }
24062         }
24063     },
24064
24065     // private
24066     onSelect : function(record, index){
24067         if(this.fireEvent('beforeselect', this, record, index) !== false){
24068             this.setFromData(index > -1 ? record.data : false);
24069             this.collapse();
24070             this.fireEvent('select', this, record, index);
24071         }
24072     },
24073
24074     /**
24075      * Returns the currently selected field value or empty string if no value is set.
24076      * @return {String} value The selected value
24077      */
24078     getValue : function(){
24079         if(this.valueField){
24080             return typeof this.value != 'undefined' ? this.value : '';
24081         }else{
24082             return Roo.form.ComboBox.superclass.getValue.call(this);
24083         }
24084     },
24085
24086     /**
24087      * Clears any text/value currently set in the field
24088      */
24089     clearValue : function(){
24090         if(this.hiddenField){
24091             this.hiddenField.value = '';
24092         }
24093         this.value = '';
24094         this.setRawValue('');
24095         this.lastSelectionText = '';
24096         
24097     },
24098
24099     /**
24100      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
24101      * will be displayed in the field.  If the value does not match the data value of an existing item,
24102      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
24103      * Otherwise the field will be blank (although the value will still be set).
24104      * @param {String} value The value to match
24105      */
24106     setValue : function(v){
24107         var text = v;
24108         if(this.valueField){
24109             var r = this.findRecord(this.valueField, v);
24110             if(r){
24111                 text = r.data[this.displayField];
24112             }else if(this.valueNotFoundText !== undefined){
24113                 text = this.valueNotFoundText;
24114             }
24115         }
24116         this.lastSelectionText = text;
24117         if(this.hiddenField){
24118             this.hiddenField.value = v;
24119         }
24120         Roo.form.ComboBox.superclass.setValue.call(this, text);
24121         this.value = v;
24122     },
24123     /**
24124      * @property {Object} the last set data for the element
24125      */
24126     
24127     lastData : false,
24128     /**
24129      * Sets the value of the field based on a object which is related to the record format for the store.
24130      * @param {Object} value the value to set as. or false on reset?
24131      */
24132     setFromData : function(o){
24133         var dv = ''; // display value
24134         var vv = ''; // value value..
24135         this.lastData = o;
24136         if (this.displayField) {
24137             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
24138         } else {
24139             // this is an error condition!!!
24140             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
24141         }
24142         
24143         if(this.valueField){
24144             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
24145         }
24146         if(this.hiddenField){
24147             this.hiddenField.value = vv;
24148             
24149             this.lastSelectionText = dv;
24150             Roo.form.ComboBox.superclass.setValue.call(this, dv);
24151             this.value = vv;
24152             return;
24153         }
24154         // no hidden field.. - we store the value in 'value', but still display
24155         // display field!!!!
24156         this.lastSelectionText = dv;
24157         Roo.form.ComboBox.superclass.setValue.call(this, dv);
24158         this.value = vv;
24159         
24160         
24161     },
24162     // private
24163     reset : function(){
24164         // overridden so that last data is reset..
24165         this.setValue(this.originalValue);
24166         this.clearInvalid();
24167         this.lastData = false;
24168         if (this.view) {
24169             this.view.clearSelections();
24170         }
24171     },
24172     // private
24173     findRecord : function(prop, value){
24174         var record;
24175         if(this.store.getCount() > 0){
24176             this.store.each(function(r){
24177                 if(r.data[prop] == value){
24178                     record = r;
24179                     return false;
24180                 }
24181                 return true;
24182             });
24183         }
24184         return record;
24185     },
24186     
24187     getName: function()
24188     {
24189         // returns hidden if it's set..
24190         if (!this.rendered) {return ''};
24191         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
24192         
24193     },
24194     // private
24195     onViewMove : function(e, t){
24196         this.inKeyMode = false;
24197     },
24198
24199     // private
24200     onViewOver : function(e, t){
24201         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24202             return;
24203         }
24204         var item = this.view.findItemFromChild(t);
24205         if(item){
24206             var index = this.view.indexOf(item);
24207             this.select(index, false);
24208         }
24209     },
24210
24211     // private
24212     onViewClick : function(doFocus)
24213     {
24214         var index = this.view.getSelectedIndexes()[0];
24215         var r = this.store.getAt(index);
24216         if(r){
24217             this.onSelect(r, index);
24218         }
24219         if(doFocus !== false && !this.blockFocus){
24220             this.el.focus();
24221         }
24222     },
24223
24224     // private
24225     restrictHeight : function(){
24226         this.innerList.dom.style.height = '';
24227         var inner = this.innerList.dom;
24228         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24229         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24230         this.list.beginUpdate();
24231         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24232         this.list.alignTo(this.el, this.listAlign);
24233         this.list.endUpdate();
24234     },
24235
24236     // private
24237     onEmptyResults : function(){
24238         this.collapse();
24239     },
24240
24241     /**
24242      * Returns true if the dropdown list is expanded, else false.
24243      */
24244     isExpanded : function(){
24245         return this.list.isVisible();
24246     },
24247
24248     /**
24249      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24250      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24251      * @param {String} value The data value of the item to select
24252      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24253      * selected item if it is not currently in view (defaults to true)
24254      * @return {Boolean} True if the value matched an item in the list, else false
24255      */
24256     selectByValue : function(v, scrollIntoView){
24257         if(v !== undefined && v !== null){
24258             var r = this.findRecord(this.valueField || this.displayField, v);
24259             if(r){
24260                 this.select(this.store.indexOf(r), scrollIntoView);
24261                 return true;
24262             }
24263         }
24264         return false;
24265     },
24266
24267     /**
24268      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24269      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24270      * @param {Number} index The zero-based index of the list item to select
24271      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24272      * selected item if it is not currently in view (defaults to true)
24273      */
24274     select : function(index, scrollIntoView){
24275         this.selectedIndex = index;
24276         this.view.select(index);
24277         if(scrollIntoView !== false){
24278             var el = this.view.getNode(index);
24279             if(el){
24280                 this.innerList.scrollChildIntoView(el, false);
24281             }
24282         }
24283     },
24284
24285     // private
24286     selectNext : function(){
24287         var ct = this.store.getCount();
24288         if(ct > 0){
24289             if(this.selectedIndex == -1){
24290                 this.select(0);
24291             }else if(this.selectedIndex < ct-1){
24292                 this.select(this.selectedIndex+1);
24293             }
24294         }
24295     },
24296
24297     // private
24298     selectPrev : function(){
24299         var ct = this.store.getCount();
24300         if(ct > 0){
24301             if(this.selectedIndex == -1){
24302                 this.select(0);
24303             }else if(this.selectedIndex != 0){
24304                 this.select(this.selectedIndex-1);
24305             }
24306         }
24307     },
24308
24309     // private
24310     onKeyUp : function(e){
24311         if(this.editable !== false && !e.isSpecialKey()){
24312             this.lastKey = e.getKey();
24313             this.dqTask.delay(this.queryDelay);
24314         }
24315     },
24316
24317     // private
24318     validateBlur : function(){
24319         return !this.list || !this.list.isVisible();   
24320     },
24321
24322     // private
24323     initQuery : function(){
24324         this.doQuery(this.getRawValue());
24325     },
24326
24327     // private
24328     doForce : function(){
24329         if(this.el.dom.value.length > 0){
24330             this.el.dom.value =
24331                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24332              
24333         }
24334     },
24335
24336     /**
24337      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
24338      * query allowing the query action to be canceled if needed.
24339      * @param {String} query The SQL query to execute
24340      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24341      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
24342      * saved in the current store (defaults to false)
24343      */
24344     doQuery : function(q, forceAll){
24345         if(q === undefined || q === null){
24346             q = '';
24347         }
24348         var qe = {
24349             query: q,
24350             forceAll: forceAll,
24351             combo: this,
24352             cancel:false
24353         };
24354         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24355             return false;
24356         }
24357         q = qe.query;
24358         forceAll = qe.forceAll;
24359         if(forceAll === true || (q.length >= this.minChars)){
24360             if(this.lastQuery != q || this.alwaysQuery){
24361                 this.lastQuery = q;
24362                 if(this.mode == 'local'){
24363                     this.selectedIndex = -1;
24364                     if(forceAll){
24365                         this.store.clearFilter();
24366                     }else{
24367                         this.store.filter(this.displayField, q);
24368                     }
24369                     this.onLoad();
24370                 }else{
24371                     this.store.baseParams[this.queryParam] = q;
24372                     this.store.load({
24373                         params: this.getParams(q)
24374                     });
24375                     this.expand();
24376                 }
24377             }else{
24378                 this.selectedIndex = -1;
24379                 this.onLoad();   
24380             }
24381         }
24382     },
24383
24384     // private
24385     getParams : function(q){
24386         var p = {};
24387         //p[this.queryParam] = q;
24388         if(this.pageSize){
24389             p.start = 0;
24390             p.limit = this.pageSize;
24391         }
24392         return p;
24393     },
24394
24395     /**
24396      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24397      */
24398     collapse : function(){
24399         if(!this.isExpanded()){
24400             return;
24401         }
24402         this.list.hide();
24403         Roo.get(document).un('mousedown', this.collapseIf, this);
24404         Roo.get(document).un('mousewheel', this.collapseIf, this);
24405         if (!this.editable) {
24406             Roo.get(document).un('keydown', this.listKeyPress, this);
24407         }
24408         this.fireEvent('collapse', this);
24409     },
24410
24411     // private
24412     collapseIf : function(e){
24413         if(!e.within(this.wrap) && !e.within(this.list)){
24414             this.collapse();
24415         }
24416     },
24417
24418     /**
24419      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24420      */
24421     expand : function(){
24422         if(this.isExpanded() || !this.hasFocus){
24423             return;
24424         }
24425         this.list.alignTo(this.el, this.listAlign);
24426         this.list.show();
24427         Roo.get(document).on('mousedown', this.collapseIf, this);
24428         Roo.get(document).on('mousewheel', this.collapseIf, this);
24429         if (!this.editable) {
24430             Roo.get(document).on('keydown', this.listKeyPress, this);
24431         }
24432         
24433         this.fireEvent('expand', this);
24434     },
24435
24436     // private
24437     // Implements the default empty TriggerField.onTriggerClick function
24438     onTriggerClick : function(){
24439         if(this.disabled){
24440             return;
24441         }
24442         if(this.isExpanded()){
24443             this.collapse();
24444             if (!this.blockFocus) {
24445                 this.el.focus();
24446             }
24447             
24448         }else {
24449             this.hasFocus = true;
24450             if(this.triggerAction == 'all') {
24451                 this.doQuery(this.allQuery, true);
24452             } else {
24453                 this.doQuery(this.getRawValue());
24454             }
24455             if (!this.blockFocus) {
24456                 this.el.focus();
24457             }
24458         }
24459     },
24460     listKeyPress : function(e)
24461     {
24462         //Roo.log('listkeypress');
24463         // scroll to first matching element based on key pres..
24464         if (e.isSpecialKey()) {
24465             return false;
24466         }
24467         var k = String.fromCharCode(e.getKey()).toUpperCase();
24468         //Roo.log(k);
24469         var match  = false;
24470         var csel = this.view.getSelectedNodes();
24471         var cselitem = false;
24472         if (csel.length) {
24473             var ix = this.view.indexOf(csel[0]);
24474             cselitem  = this.store.getAt(ix);
24475             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24476                 cselitem = false;
24477             }
24478             
24479         }
24480         
24481         this.store.each(function(v) { 
24482             if (cselitem) {
24483                 // start at existing selection.
24484                 if (cselitem.id == v.id) {
24485                     cselitem = false;
24486                 }
24487                 return;
24488             }
24489                 
24490             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24491                 match = this.store.indexOf(v);
24492                 return false;
24493             }
24494         }, this);
24495         
24496         if (match === false) {
24497             return true; // no more action?
24498         }
24499         // scroll to?
24500         this.view.select(match);
24501         var sn = Roo.get(this.view.getSelectedNodes()[0])
24502         sn.scrollIntoView(sn.dom.parentNode, false);
24503     }
24504
24505     /** 
24506     * @cfg {Boolean} grow 
24507     * @hide 
24508     */
24509     /** 
24510     * @cfg {Number} growMin 
24511     * @hide 
24512     */
24513     /** 
24514     * @cfg {Number} growMax 
24515     * @hide 
24516     */
24517     /**
24518      * @hide
24519      * @method autoSize
24520      */
24521 });/*
24522  * Copyright(c) 2010-2012, Roo J Solutions Limited
24523  *
24524  * Licence LGPL
24525  *
24526  */
24527
24528 /**
24529  * @class Roo.form.ComboBoxArray
24530  * @extends Roo.form.TextField
24531  * A facebook style adder... for lists of email / people / countries  etc...
24532  * pick multiple items from a combo box, and shows each one.
24533  *
24534  *  Fred [x]  Brian [x]  [Pick another |v]
24535  *
24536  *
24537  *  For this to work: it needs various extra information
24538  *    - normal combo problay has
24539  *      name, hiddenName
24540  *    + displayField, valueField
24541  *
24542  *    For our purpose...
24543  *
24544  *
24545  *   If we change from 'extends' to wrapping...
24546  *   
24547  *  
24548  *
24549  
24550  
24551  * @constructor
24552  * Create a new ComboBoxArray.
24553  * @param {Object} config Configuration options
24554  */
24555  
24556
24557 Roo.form.ComboBoxArray = function(config)
24558 {
24559     
24560     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24561     
24562     this.items = new Roo.util.MixedCollection(false);
24563     
24564     // construct the child combo...
24565     
24566     
24567     
24568     
24569    
24570     
24571 }
24572
24573  
24574 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24575
24576     /**
24577      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24578      */
24579     
24580     lastData : false,
24581     
24582     // behavies liek a hiddne field
24583     inputType:      'hidden',
24584     /**
24585      * @cfg {Number} width The width of the box that displays the selected element
24586      */ 
24587     width:          300,
24588
24589     
24590     
24591     /**
24592      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24593      */
24594     name : false,
24595     /**
24596      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24597      */
24598     hiddenName : false,
24599     
24600     
24601     // private the array of items that are displayed..
24602     items  : false,
24603     // private - the hidden field el.
24604     hiddenEl : false,
24605     // private - the filed el..
24606     el : false,
24607     
24608     //validateValue : function() { return true; }, // all values are ok!
24609     //onAddClick: function() { },
24610     
24611     onRender : function(ct, position) 
24612     {
24613         
24614         // create the standard hidden element
24615         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24616         
24617         
24618         // give fake names to child combo;
24619         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24620         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24621         
24622         this.combo = Roo.factory(this.combo, Roo.form);
24623         this.combo.onRender(ct, position);
24624         if (typeof(this.combo.width) != 'undefined') {
24625             this.combo.onResize(this.combo.width,0);
24626         }
24627         
24628         this.combo.initEvents();
24629         
24630         // assigned so form know we need to do this..
24631         this.store          = this.combo.store;
24632         this.valueField     = this.combo.valueField;
24633         this.displayField   = this.combo.displayField ;
24634         
24635         
24636         this.combo.wrap.addClass('x-cbarray-grp');
24637         
24638         var cbwrap = this.combo.wrap.createChild(
24639             {tag: 'div', cls: 'x-cbarray-cb'},
24640             this.combo.el.dom
24641         );
24642         
24643              
24644         this.hiddenEl = this.combo.wrap.createChild({
24645             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24646         });
24647         this.el = this.combo.wrap.createChild({
24648             tag: 'input',  type:'hidden' , name: this.name, value : ''
24649         });
24650          //   this.el.dom.removeAttribute("name");
24651         
24652         
24653         this.outerWrap = this.combo.wrap;
24654         this.wrap = cbwrap;
24655         
24656         this.outerWrap.setWidth(this.width);
24657         this.outerWrap.dom.removeChild(this.el.dom);
24658         
24659         this.wrap.dom.appendChild(this.el.dom);
24660         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24661         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24662         
24663         this.combo.trigger.setStyle('position','relative');
24664         this.combo.trigger.setStyle('left', '0px');
24665         this.combo.trigger.setStyle('top', '2px');
24666         
24667         this.combo.el.setStyle('vertical-align', 'text-bottom');
24668         
24669         //this.trigger.setStyle('vertical-align', 'top');
24670         
24671         // this should use the code from combo really... on('add' ....)
24672         if (this.adder) {
24673             
24674         
24675             this.adder = this.outerWrap.createChild(
24676                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24677             var _t = this;
24678             this.adder.on('click', function(e) {
24679                 _t.fireEvent('adderclick', this, e);
24680             }, _t);
24681         }
24682         //var _t = this;
24683         //this.adder.on('click', this.onAddClick, _t);
24684         
24685         
24686         this.combo.on('select', function(cb, rec, ix) {
24687             this.addItem(rec.data);
24688             
24689             cb.setValue('');
24690             cb.el.dom.value = '';
24691             //cb.lastData = rec.data;
24692             // add to list
24693             
24694         }, this);
24695         
24696         
24697     },
24698     
24699     
24700     getName: function()
24701     {
24702         // returns hidden if it's set..
24703         if (!this.rendered) {return ''};
24704         return  this.hiddenName ? this.hiddenName : this.name;
24705         
24706     },
24707     
24708     
24709     onResize: function(w, h){
24710         
24711         return;
24712         // not sure if this is needed..
24713         //this.combo.onResize(w,h);
24714         
24715         if(typeof w != 'number'){
24716             // we do not handle it!?!?
24717             return;
24718         }
24719         var tw = this.combo.trigger.getWidth();
24720         tw += this.addicon ? this.addicon.getWidth() : 0;
24721         tw += this.editicon ? this.editicon.getWidth() : 0;
24722         var x = w - tw;
24723         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24724             
24725         this.combo.trigger.setStyle('left', '0px');
24726         
24727         if(this.list && this.listWidth === undefined){
24728             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24729             this.list.setWidth(lw);
24730             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24731         }
24732         
24733     
24734         
24735     },
24736     
24737     addItem: function(rec)
24738     {
24739         var valueField = this.combo.valueField;
24740         var displayField = this.combo.displayField;
24741         if (this.items.indexOfKey(rec[valueField]) > -1) {
24742             //console.log("GOT " + rec.data.id);
24743             return;
24744         }
24745         
24746         var x = new Roo.form.ComboBoxArray.Item({
24747             //id : rec[this.idField],
24748             data : rec,
24749             displayField : displayField ,
24750             tipField : displayField ,
24751             cb : this
24752         });
24753         // use the 
24754         this.items.add(rec[valueField],x);
24755         // add it before the element..
24756         this.updateHiddenEl();
24757         x.render(this.outerWrap, this.wrap.dom);
24758         // add the image handler..
24759     },
24760     
24761     updateHiddenEl : function()
24762     {
24763         this.validate();
24764         if (!this.hiddenEl) {
24765             return;
24766         }
24767         var ar = [];
24768         var idField = this.combo.valueField;
24769         
24770         this.items.each(function(f) {
24771             ar.push(f.data[idField]);
24772            
24773         });
24774         this.hiddenEl.dom.value = ar.join(',');
24775         this.validate();
24776     },
24777     
24778     reset : function()
24779     {
24780         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24781         this.items.each(function(f) {
24782            f.remove(); 
24783         });
24784         this.el.dom.value = '';
24785         if (this.hiddenEl) {
24786             this.hiddenEl.dom.value = '';
24787         }
24788         
24789     },
24790     getValue: function()
24791     {
24792         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24793     },
24794     setValue: function(v) // not a valid action - must use addItems..
24795     {
24796          
24797         this.reset();
24798         
24799         
24800         
24801         if (this.store.isLocal && (typeof(v) == 'string')) {
24802             // then we can use the store to find the values..
24803             // comma seperated at present.. this needs to allow JSON based encoding..
24804             this.hiddenEl.value  = v;
24805             var v_ar = [];
24806             Roo.each(v.split(','), function(k) {
24807                 Roo.log("CHECK " + this.valueField + ',' + k);
24808                 var li = this.store.query(this.valueField, k);
24809                 if (!li.length) {
24810                     return;
24811                 }
24812                 var add = {};
24813                 add[this.valueField] = k;
24814                 add[this.displayField] = li.item(0).data[this.displayField];
24815                 
24816                 this.addItem(add);
24817             }, this) 
24818              
24819         }
24820         if (typeof(v) == 'object') {
24821             // then let's assume it's an array of objects..
24822             Roo.each(v, function(l) {
24823                 this.addItem(l);
24824             }, this);
24825              
24826         }
24827         
24828         
24829     },
24830     setFromData: function(v)
24831     {
24832         // this recieves an object, if setValues is called.
24833         this.reset();
24834         this.el.dom.value = v[this.displayField];
24835         this.hiddenEl.dom.value = v[this.valueField];
24836         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24837             return;
24838         }
24839         var kv = v[this.valueField];
24840         var dv = v[this.displayField];
24841         kv = typeof(kv) != 'string' ? '' : kv;
24842         dv = typeof(dv) != 'string' ? '' : dv;
24843         
24844         
24845         var keys = kv.split(',');
24846         var display = dv.split(',');
24847         for (var i = 0 ; i < keys.length; i++) {
24848             
24849             add = {};
24850             add[this.valueField] = keys[i];
24851             add[this.displayField] = display[i];
24852             this.addItem(add);
24853         }
24854       
24855         
24856     },
24857     
24858     
24859     validateValue : function(value){
24860         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24861         
24862     }
24863     
24864 });
24865
24866
24867
24868 /**
24869  * @class Roo.form.ComboBoxArray.Item
24870  * @extends Roo.BoxComponent
24871  * A selected item in the list
24872  *  Fred [x]  Brian [x]  [Pick another |v]
24873  * 
24874  * @constructor
24875  * Create a new item.
24876  * @param {Object} config Configuration options
24877  */
24878  
24879 Roo.form.ComboBoxArray.Item = function(config) {
24880     config.id = Roo.id();
24881     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24882 }
24883
24884 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24885     data : {},
24886     cb: false,
24887     displayField : false,
24888     tipField : false,
24889     
24890     
24891     defaultAutoCreate : {
24892         tag: 'div',
24893         cls: 'x-cbarray-item',
24894         cn : [ 
24895             { tag: 'div' },
24896             {
24897                 tag: 'img',
24898                 width:16,
24899                 height : 16,
24900                 src : Roo.BLANK_IMAGE_URL ,
24901                 align: 'center'
24902             }
24903         ]
24904         
24905     },
24906     
24907  
24908     onRender : function(ct, position)
24909     {
24910         Roo.form.Field.superclass.onRender.call(this, ct, position);
24911         
24912         if(!this.el){
24913             var cfg = this.getAutoCreate();
24914             this.el = ct.createChild(cfg, position);
24915         }
24916         
24917         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24918         
24919         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24920             this.cb.renderer(this.data) :
24921             String.format('{0}',this.data[this.displayField]);
24922         
24923             
24924         this.el.child('div').dom.setAttribute('qtip',
24925                         String.format('{0}',this.data[this.tipField])
24926         );
24927         
24928         this.el.child('img').on('click', this.remove, this);
24929         
24930     },
24931    
24932     remove : function()
24933     {
24934         
24935         this.cb.items.remove(this);
24936         this.el.child('img').un('click', this.remove, this);
24937         this.el.remove();
24938         this.cb.updateHiddenEl();
24939     }
24940     
24941     
24942 });/*
24943  * Based on:
24944  * Ext JS Library 1.1.1
24945  * Copyright(c) 2006-2007, Ext JS, LLC.
24946  *
24947  * Originally Released Under LGPL - original licence link has changed is not relivant.
24948  *
24949  * Fork - LGPL
24950  * <script type="text/javascript">
24951  */
24952 /**
24953  * @class Roo.form.Checkbox
24954  * @extends Roo.form.Field
24955  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24956  * @constructor
24957  * Creates a new Checkbox
24958  * @param {Object} config Configuration options
24959  */
24960 Roo.form.Checkbox = function(config){
24961     Roo.form.Checkbox.superclass.constructor.call(this, config);
24962     this.addEvents({
24963         /**
24964          * @event check
24965          * Fires when the checkbox is checked or unchecked.
24966              * @param {Roo.form.Checkbox} this This checkbox
24967              * @param {Boolean} checked The new checked value
24968              */
24969         check : true
24970     });
24971 };
24972
24973 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24974     /**
24975      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24976      */
24977     focusClass : undefined,
24978     /**
24979      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24980      */
24981     fieldClass: "x-form-field",
24982     /**
24983      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24984      */
24985     checked: false,
24986     /**
24987      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24988      * {tag: "input", type: "checkbox", autocomplete: "off"})
24989      */
24990     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24991     /**
24992      * @cfg {String} boxLabel The text that appears beside the checkbox
24993      */
24994     boxLabel : "",
24995     /**
24996      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24997      */  
24998     inputValue : '1',
24999     /**
25000      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
25001      */
25002      valueOff: '0', // value when not checked..
25003
25004     actionMode : 'viewEl', 
25005     //
25006     // private
25007     itemCls : 'x-menu-check-item x-form-item',
25008     groupClass : 'x-menu-group-item',
25009     inputType : 'hidden',
25010     
25011     
25012     inSetChecked: false, // check that we are not calling self...
25013     
25014     inputElement: false, // real input element?
25015     basedOn: false, // ????
25016     
25017     isFormField: true, // not sure where this is needed!!!!
25018
25019     onResize : function(){
25020         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
25021         if(!this.boxLabel){
25022             this.el.alignTo(this.wrap, 'c-c');
25023         }
25024     },
25025
25026     initEvents : function(){
25027         Roo.form.Checkbox.superclass.initEvents.call(this);
25028         this.el.on("click", this.onClick,  this);
25029         this.el.on("change", this.onClick,  this);
25030     },
25031
25032
25033     getResizeEl : function(){
25034         return this.wrap;
25035     },
25036
25037     getPositionEl : function(){
25038         return this.wrap;
25039     },
25040
25041     // private
25042     onRender : function(ct, position){
25043         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25044         /*
25045         if(this.inputValue !== undefined){
25046             this.el.dom.value = this.inputValue;
25047         }
25048         */
25049         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25050         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25051         var viewEl = this.wrap.createChild({ 
25052             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25053         this.viewEl = viewEl;   
25054         this.wrap.on('click', this.onClick,  this); 
25055         
25056         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25057         this.el.on('propertychange', this.setFromHidden,  this);  //ie
25058         
25059         
25060         
25061         if(this.boxLabel){
25062             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25063         //    viewEl.on('click', this.onClick,  this); 
25064         }
25065         //if(this.checked){
25066             this.setChecked(this.checked);
25067         //}else{
25068             //this.checked = this.el.dom;
25069         //}
25070
25071     },
25072
25073     // private
25074     initValue : Roo.emptyFn,
25075
25076     /**
25077      * Returns the checked state of the checkbox.
25078      * @return {Boolean} True if checked, else false
25079      */
25080     getValue : function(){
25081         if(this.el){
25082             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
25083         }
25084         return this.valueOff;
25085         
25086     },
25087
25088         // private
25089     onClick : function(){ 
25090         this.setChecked(!this.checked);
25091
25092         //if(this.el.dom.checked != this.checked){
25093         //    this.setValue(this.el.dom.checked);
25094        // }
25095     },
25096
25097     /**
25098      * Sets the checked state of the checkbox.
25099      * On is always based on a string comparison between inputValue and the param.
25100      * @param {Boolean/String} value - the value to set 
25101      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
25102      */
25103     setValue : function(v,suppressEvent){
25104         
25105         
25106         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
25107         //if(this.el && this.el.dom){
25108         //    this.el.dom.checked = this.checked;
25109         //    this.el.dom.defaultChecked = this.checked;
25110         //}
25111         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
25112         //this.fireEvent("check", this, this.checked);
25113     },
25114     // private..
25115     setChecked : function(state,suppressEvent)
25116     {
25117         if (this.inSetChecked) {
25118             this.checked = state;
25119             return;
25120         }
25121         
25122     
25123         if(this.wrap){
25124             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
25125         }
25126         this.checked = state;
25127         if(suppressEvent !== true){
25128             this.fireEvent('check', this, state);
25129         }
25130         this.inSetChecked = true;
25131         this.el.dom.value = state ? this.inputValue : this.valueOff;
25132         this.inSetChecked = false;
25133         
25134     },
25135     // handle setting of hidden value by some other method!!?!?
25136     setFromHidden: function()
25137     {
25138         if(!this.el){
25139             return;
25140         }
25141         //console.log("SET FROM HIDDEN");
25142         //alert('setFrom hidden');
25143         this.setValue(this.el.dom.value);
25144     },
25145     
25146     onDestroy : function()
25147     {
25148         if(this.viewEl){
25149             Roo.get(this.viewEl).remove();
25150         }
25151          
25152         Roo.form.Checkbox.superclass.onDestroy.call(this);
25153     }
25154
25155 });/*
25156  * Based on:
25157  * Ext JS Library 1.1.1
25158  * Copyright(c) 2006-2007, Ext JS, LLC.
25159  *
25160  * Originally Released Under LGPL - original licence link has changed is not relivant.
25161  *
25162  * Fork - LGPL
25163  * <script type="text/javascript">
25164  */
25165  
25166 /**
25167  * @class Roo.form.Radio
25168  * @extends Roo.form.Checkbox
25169  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
25170  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
25171  * @constructor
25172  * Creates a new Radio
25173  * @param {Object} config Configuration options
25174  */
25175 Roo.form.Radio = function(){
25176     Roo.form.Radio.superclass.constructor.apply(this, arguments);
25177 };
25178 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
25179     inputType: 'radio',
25180
25181     /**
25182      * If this radio is part of a group, it will return the selected value
25183      * @return {String}
25184      */
25185     getGroupValue : function(){
25186         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
25187     },
25188     
25189     
25190     onRender : function(ct, position){
25191         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25192         
25193         if(this.inputValue !== undefined){
25194             this.el.dom.value = this.inputValue;
25195         }
25196          
25197         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25198         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25199         //var viewEl = this.wrap.createChild({ 
25200         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25201         //this.viewEl = viewEl;   
25202         //this.wrap.on('click', this.onClick,  this); 
25203         
25204         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25205         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
25206         
25207         
25208         
25209         if(this.boxLabel){
25210             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25211         //    viewEl.on('click', this.onClick,  this); 
25212         }
25213          if(this.checked){
25214             this.el.dom.checked =   'checked' ;
25215         }
25216          
25217     } 
25218     
25219     
25220 });//<script type="text/javascript">
25221
25222 /*
25223  * Ext JS Library 1.1.1
25224  * Copyright(c) 2006-2007, Ext JS, LLC.
25225  * licensing@extjs.com
25226  * 
25227  * http://www.extjs.com/license
25228  */
25229  
25230  /*
25231   * 
25232   * Known bugs:
25233   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25234   * - IE ? - no idea how much works there.
25235   * 
25236   * 
25237   * 
25238   */
25239  
25240
25241 /**
25242  * @class Ext.form.HtmlEditor
25243  * @extends Ext.form.Field
25244  * Provides a lightweight HTML Editor component.
25245  *
25246  * This has been tested on Fireforx / Chrome.. IE may not be so great..
25247  * 
25248  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25249  * supported by this editor.</b><br/><br/>
25250  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25251  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25252  */
25253 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25254       /**
25255      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25256      */
25257     toolbars : false,
25258     /**
25259      * @cfg {String} createLinkText The default text for the create link prompt
25260      */
25261     createLinkText : 'Please enter the URL for the link:',
25262     /**
25263      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25264      */
25265     defaultLinkValue : 'http:/'+'/',
25266    
25267      /**
25268      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25269      *                        Roo.resizable.
25270      */
25271     resizable : false,
25272      /**
25273      * @cfg {Number} height (in pixels)
25274      */   
25275     height: 300,
25276    /**
25277      * @cfg {Number} width (in pixels)
25278      */   
25279     width: 500,
25280     
25281     /**
25282      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25283      * 
25284      */
25285     stylesheets: false,
25286     
25287     // id of frame..
25288     frameId: false,
25289     
25290     // private properties
25291     validationEvent : false,
25292     deferHeight: true,
25293     initialized : false,
25294     activated : false,
25295     sourceEditMode : false,
25296     onFocus : Roo.emptyFn,
25297     iframePad:3,
25298     hideMode:'offsets',
25299     
25300     defaultAutoCreate : { // modified by initCompnoent..
25301         tag: "textarea",
25302         style:"width:500px;height:300px;",
25303         autocomplete: "off"
25304     },
25305
25306     // private
25307     initComponent : function(){
25308         this.addEvents({
25309             /**
25310              * @event initialize
25311              * Fires when the editor is fully initialized (including the iframe)
25312              * @param {HtmlEditor} this
25313              */
25314             initialize: true,
25315             /**
25316              * @event activate
25317              * Fires when the editor is first receives the focus. Any insertion must wait
25318              * until after this event.
25319              * @param {HtmlEditor} this
25320              */
25321             activate: true,
25322              /**
25323              * @event beforesync
25324              * Fires before the textarea is updated with content from the editor iframe. Return false
25325              * to cancel the sync.
25326              * @param {HtmlEditor} this
25327              * @param {String} html
25328              */
25329             beforesync: true,
25330              /**
25331              * @event beforepush
25332              * Fires before the iframe editor is updated with content from the textarea. Return false
25333              * to cancel the push.
25334              * @param {HtmlEditor} this
25335              * @param {String} html
25336              */
25337             beforepush: true,
25338              /**
25339              * @event sync
25340              * Fires when the textarea is updated with content from the editor iframe.
25341              * @param {HtmlEditor} this
25342              * @param {String} html
25343              */
25344             sync: true,
25345              /**
25346              * @event push
25347              * Fires when the iframe editor is updated with content from the textarea.
25348              * @param {HtmlEditor} this
25349              * @param {String} html
25350              */
25351             push: true,
25352              /**
25353              * @event editmodechange
25354              * Fires when the editor switches edit modes
25355              * @param {HtmlEditor} this
25356              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25357              */
25358             editmodechange: true,
25359             /**
25360              * @event editorevent
25361              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25362              * @param {HtmlEditor} this
25363              */
25364             editorevent: true
25365         });
25366         this.defaultAutoCreate =  {
25367             tag: "textarea",
25368             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25369             autocomplete: "off"
25370         };
25371     },
25372
25373     /**
25374      * Protected method that will not generally be called directly. It
25375      * is called when the editor creates its toolbar. Override this method if you need to
25376      * add custom toolbar buttons.
25377      * @param {HtmlEditor} editor
25378      */
25379     createToolbar : function(editor){
25380         if (!editor.toolbars || !editor.toolbars.length) {
25381             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25382         }
25383         
25384         for (var i =0 ; i < editor.toolbars.length;i++) {
25385             editor.toolbars[i] = Roo.factory(
25386                     typeof(editor.toolbars[i]) == 'string' ?
25387                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
25388                 Roo.form.HtmlEditor);
25389             editor.toolbars[i].init(editor);
25390         }
25391          
25392         
25393     },
25394
25395     /**
25396      * Protected method that will not generally be called directly. It
25397      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25398      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25399      */
25400     getDocMarkup : function(){
25401         // body styles..
25402         var st = '';
25403         if (this.stylesheets === false) {
25404             
25405             Roo.get(document.head).select('style').each(function(node) {
25406                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25407             });
25408             
25409             Roo.get(document.head).select('link').each(function(node) { 
25410                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25411             });
25412             
25413         } else if (!this.stylesheets.length) {
25414                 // simple..
25415                 st = '<style type="text/css">' +
25416                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25417                    '</style>';
25418         } else {
25419             Roo.each(this.stylesheets, function(s) {
25420                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25421             });
25422             
25423         }
25424         
25425         st +=  '<style type="text/css">' +
25426             'IMG { cursor: pointer } ' +
25427         '</style>';
25428
25429         
25430         return '<html><head>' + st  +
25431             //<style type="text/css">' +
25432             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25433             //'</style>' +
25434             ' </head><body class="roo-htmleditor-body"></body></html>';
25435     },
25436
25437     // private
25438     onRender : function(ct, position)
25439     {
25440         var _t = this;
25441         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25442         this.el.dom.style.border = '0 none';
25443         this.el.dom.setAttribute('tabIndex', -1);
25444         this.el.addClass('x-hidden');
25445         if(Roo.isIE){ // fix IE 1px bogus margin
25446             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25447         }
25448         this.wrap = this.el.wrap({
25449             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25450         });
25451         
25452         if (this.resizable) {
25453             this.resizeEl = new Roo.Resizable(this.wrap, {
25454                 pinned : true,
25455                 wrap: true,
25456                 dynamic : true,
25457                 minHeight : this.height,
25458                 height: this.height,
25459                 handles : this.resizable,
25460                 width: this.width,
25461                 listeners : {
25462                     resize : function(r, w, h) {
25463                         _t.onResize(w,h); // -something
25464                     }
25465                 }
25466             });
25467             
25468         }
25469
25470         this.frameId = Roo.id();
25471         
25472         this.createToolbar(this);
25473         
25474       
25475         
25476         var iframe = this.wrap.createChild({
25477             tag: 'iframe',
25478             id: this.frameId,
25479             name: this.frameId,
25480             frameBorder : 'no',
25481             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25482         }, this.el
25483         );
25484         
25485        // console.log(iframe);
25486         //this.wrap.dom.appendChild(iframe);
25487
25488         this.iframe = iframe.dom;
25489
25490          this.assignDocWin();
25491         
25492         this.doc.designMode = 'on';
25493        
25494         this.doc.open();
25495         this.doc.write(this.getDocMarkup());
25496         this.doc.close();
25497
25498         
25499         var task = { // must defer to wait for browser to be ready
25500             run : function(){
25501                 //console.log("run task?" + this.doc.readyState);
25502                 this.assignDocWin();
25503                 if(this.doc.body || this.doc.readyState == 'complete'){
25504                     try {
25505                         this.doc.designMode="on";
25506                     } catch (e) {
25507                         return;
25508                     }
25509                     Roo.TaskMgr.stop(task);
25510                     this.initEditor.defer(10, this);
25511                 }
25512             },
25513             interval : 10,
25514             duration:10000,
25515             scope: this
25516         };
25517         Roo.TaskMgr.start(task);
25518
25519         if(!this.width){
25520             this.setSize(this.wrap.getSize());
25521         }
25522         if (this.resizeEl) {
25523             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25524             // should trigger onReize..
25525         }
25526     },
25527
25528     // private
25529     onResize : function(w, h)
25530     {
25531         //Roo.log('resize: ' +w + ',' + h );
25532         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25533         if(this.el && this.iframe){
25534             if(typeof w == 'number'){
25535                 var aw = w - this.wrap.getFrameWidth('lr');
25536                 this.el.setWidth(this.adjustWidth('textarea', aw));
25537                 this.iframe.style.width = aw + 'px';
25538             }
25539             if(typeof h == 'number'){
25540                 var tbh = 0;
25541                 for (var i =0; i < this.toolbars.length;i++) {
25542                     // fixme - ask toolbars for heights?
25543                     tbh += this.toolbars[i].tb.el.getHeight();
25544                     if (this.toolbars[i].footer) {
25545                         tbh += this.toolbars[i].footer.el.getHeight();
25546                     }
25547                 }
25548                 
25549                 
25550                 
25551                 
25552                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25553                 ah -= 5; // knock a few pixes off for look..
25554                 this.el.setHeight(this.adjustWidth('textarea', ah));
25555                 this.iframe.style.height = ah + 'px';
25556                 if(this.doc){
25557                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25558                 }
25559             }
25560         }
25561     },
25562
25563     /**
25564      * Toggles the editor between standard and source edit mode.
25565      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25566      */
25567     toggleSourceEdit : function(sourceEditMode){
25568         
25569         this.sourceEditMode = sourceEditMode === true;
25570         
25571         if(this.sourceEditMode){
25572 //            Roo.log('in');
25573 //            Roo.log(this.syncValue());
25574             this.syncValue();
25575             this.iframe.className = 'x-hidden';
25576             this.el.removeClass('x-hidden');
25577             this.el.dom.removeAttribute('tabIndex');
25578             this.el.focus();
25579         }else{
25580 //            Roo.log('out')
25581 //            Roo.log(this.pushValue()); 
25582             this.pushValue();
25583             this.iframe.className = '';
25584             this.el.addClass('x-hidden');
25585             this.el.dom.setAttribute('tabIndex', -1);
25586             this.deferFocus();
25587         }
25588         this.setSize(this.wrap.getSize());
25589         this.fireEvent('editmodechange', this, this.sourceEditMode);
25590     },
25591
25592     // private used internally
25593     createLink : function(){
25594         var url = prompt(this.createLinkText, this.defaultLinkValue);
25595         if(url && url != 'http:/'+'/'){
25596             this.relayCmd('createlink', url);
25597         }
25598     },
25599
25600     // private (for BoxComponent)
25601     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25602
25603     // private (for BoxComponent)
25604     getResizeEl : function(){
25605         return this.wrap;
25606     },
25607
25608     // private (for BoxComponent)
25609     getPositionEl : function(){
25610         return this.wrap;
25611     },
25612
25613     // private
25614     initEvents : function(){
25615         this.originalValue = this.getValue();
25616     },
25617
25618     /**
25619      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25620      * @method
25621      */
25622     markInvalid : Roo.emptyFn,
25623     /**
25624      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25625      * @method
25626      */
25627     clearInvalid : Roo.emptyFn,
25628
25629     setValue : function(v){
25630         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25631         this.pushValue();
25632     },
25633
25634     /**
25635      * Protected method that will not generally be called directly. If you need/want
25636      * custom HTML cleanup, this is the method you should override.
25637      * @param {String} html The HTML to be cleaned
25638      * return {String} The cleaned HTML
25639      */
25640     cleanHtml : function(html){
25641         html = String(html);
25642         if(html.length > 5){
25643             if(Roo.isSafari){ // strip safari nonsense
25644                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25645             }
25646         }
25647         if(html == '&nbsp;'){
25648             html = '';
25649         }
25650         return html;
25651     },
25652
25653     /**
25654      * Protected method that will not generally be called directly. Syncs the contents
25655      * of the editor iframe with the textarea.
25656      */
25657     syncValue : function(){
25658         if(this.initialized){
25659             var bd = (this.doc.body || this.doc.documentElement);
25660             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25661             var html = bd.innerHTML;
25662             if(Roo.isSafari){
25663                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25664                 var m = bs.match(/text-align:(.*?);/i);
25665                 if(m && m[1]){
25666                     html = '<div style="'+m[0]+'">' + html + '</div>';
25667                 }
25668             }
25669             html = this.cleanHtml(html);
25670             // fix up the special chars.. normaly like back quotes in word...
25671             // however we do not want to do this with chinese..
25672             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25673                 var cc = b.charCodeAt();
25674                 if (
25675                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25676                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25677                     (cc >= 0xf900 && cc < 0xfb00 )
25678                 ) {
25679                         return b;
25680                 }
25681                 return "&#"+cc+";" 
25682             });
25683             if(this.fireEvent('beforesync', this, html) !== false){
25684                 this.el.dom.value = html;
25685                 this.fireEvent('sync', this, html);
25686             }
25687         }
25688     },
25689
25690     /**
25691      * Protected method that will not generally be called directly. Pushes the value of the textarea
25692      * into the iframe editor.
25693      */
25694     pushValue : function(){
25695         if(this.initialized){
25696             var v = this.el.dom.value;
25697             
25698             if(v.length < 1){
25699                 v = '&#160;';
25700             }
25701             
25702             if(this.fireEvent('beforepush', this, v) !== false){
25703                 var d = (this.doc.body || this.doc.documentElement);
25704                 d.innerHTML = v;
25705                 this.cleanUpPaste();
25706                 this.el.dom.value = d.innerHTML;
25707                 this.fireEvent('push', this, v);
25708             }
25709         }
25710     },
25711
25712     // private
25713     deferFocus : function(){
25714         this.focus.defer(10, this);
25715     },
25716
25717     // doc'ed in Field
25718     focus : function(){
25719         if(this.win && !this.sourceEditMode){
25720             this.win.focus();
25721         }else{
25722             this.el.focus();
25723         }
25724     },
25725     
25726     assignDocWin: function()
25727     {
25728         var iframe = this.iframe;
25729         
25730          if(Roo.isIE){
25731             this.doc = iframe.contentWindow.document;
25732             this.win = iframe.contentWindow;
25733         } else {
25734             if (!Roo.get(this.frameId)) {
25735                 return;
25736             }
25737             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25738             this.win = Roo.get(this.frameId).dom.contentWindow;
25739         }
25740     },
25741     
25742     // private
25743     initEditor : function(){
25744         //console.log("INIT EDITOR");
25745         this.assignDocWin();
25746         
25747         
25748         
25749         this.doc.designMode="on";
25750         this.doc.open();
25751         this.doc.write(this.getDocMarkup());
25752         this.doc.close();
25753         
25754         var dbody = (this.doc.body || this.doc.documentElement);
25755         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25756         // this copies styles from the containing element into thsi one..
25757         // not sure why we need all of this..
25758         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25759         ss['background-attachment'] = 'fixed'; // w3c
25760         dbody.bgProperties = 'fixed'; // ie
25761         Roo.DomHelper.applyStyles(dbody, ss);
25762         Roo.EventManager.on(this.doc, {
25763             //'mousedown': this.onEditorEvent,
25764             'mouseup': this.onEditorEvent,
25765             'dblclick': this.onEditorEvent,
25766             'click': this.onEditorEvent,
25767             'keyup': this.onEditorEvent,
25768             buffer:100,
25769             scope: this
25770         });
25771         if(Roo.isGecko){
25772             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25773         }
25774         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25775             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25776         }
25777         this.initialized = true;
25778
25779         this.fireEvent('initialize', this);
25780         this.pushValue();
25781     },
25782
25783     // private
25784     onDestroy : function(){
25785         
25786         
25787         
25788         if(this.rendered){
25789             
25790             for (var i =0; i < this.toolbars.length;i++) {
25791                 // fixme - ask toolbars for heights?
25792                 this.toolbars[i].onDestroy();
25793             }
25794             
25795             this.wrap.dom.innerHTML = '';
25796             this.wrap.remove();
25797         }
25798     },
25799
25800     // private
25801     onFirstFocus : function(){
25802         
25803         this.assignDocWin();
25804         
25805         
25806         this.activated = true;
25807         for (var i =0; i < this.toolbars.length;i++) {
25808             this.toolbars[i].onFirstFocus();
25809         }
25810        
25811         if(Roo.isGecko){ // prevent silly gecko errors
25812             this.win.focus();
25813             var s = this.win.getSelection();
25814             if(!s.focusNode || s.focusNode.nodeType != 3){
25815                 var r = s.getRangeAt(0);
25816                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25817                 r.collapse(true);
25818                 this.deferFocus();
25819             }
25820             try{
25821                 this.execCmd('useCSS', true);
25822                 this.execCmd('styleWithCSS', false);
25823             }catch(e){}
25824         }
25825         this.fireEvent('activate', this);
25826     },
25827
25828     // private
25829     adjustFont: function(btn){
25830         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25831         //if(Roo.isSafari){ // safari
25832         //    adjust *= 2;
25833        // }
25834         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25835         if(Roo.isSafari){ // safari
25836             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25837             v =  (v < 10) ? 10 : v;
25838             v =  (v > 48) ? 48 : v;
25839             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25840             
25841         }
25842         
25843         
25844         v = Math.max(1, v+adjust);
25845         
25846         this.execCmd('FontSize', v  );
25847     },
25848
25849     onEditorEvent : function(e){
25850         this.fireEvent('editorevent', this, e);
25851       //  this.updateToolbar();
25852         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25853     },
25854
25855     insertTag : function(tg)
25856     {
25857         // could be a bit smarter... -> wrap the current selected tRoo..
25858         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25859             
25860             range = this.createRange(this.getSelection());
25861             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25862             wrappingNode.appendChild(range.extractContents());
25863             range.insertNode(wrappingNode);
25864
25865             return;
25866             
25867             
25868             
25869         }
25870         this.execCmd("formatblock",   tg);
25871         
25872     },
25873     
25874     insertText : function(txt)
25875     {
25876         
25877         
25878         var range = this.createRange();
25879         range.deleteContents();
25880                //alert(Sender.getAttribute('label'));
25881                
25882         range.insertNode(this.doc.createTextNode(txt));
25883     } ,
25884     
25885     // private
25886     relayBtnCmd : function(btn){
25887         this.relayCmd(btn.cmd);
25888     },
25889
25890     /**
25891      * Executes a Midas editor command on the editor document and performs necessary focus and
25892      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25893      * @param {String} cmd The Midas command
25894      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25895      */
25896     relayCmd : function(cmd, value){
25897         this.win.focus();
25898         this.execCmd(cmd, value);
25899         this.fireEvent('editorevent', this);
25900         //this.updateToolbar();
25901         this.deferFocus();
25902     },
25903
25904     /**
25905      * Executes a Midas editor command directly on the editor document.
25906      * For visual commands, you should use {@link #relayCmd} instead.
25907      * <b>This should only be called after the editor is initialized.</b>
25908      * @param {String} cmd The Midas command
25909      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25910      */
25911     execCmd : function(cmd, value){
25912         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25913         this.syncValue();
25914     },
25915  
25916  
25917    
25918     /**
25919      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25920      * to insert tRoo.
25921      * @param {String} text | dom node.. 
25922      */
25923     insertAtCursor : function(text)
25924     {
25925         
25926         
25927         
25928         if(!this.activated){
25929             return;
25930         }
25931         /*
25932         if(Roo.isIE){
25933             this.win.focus();
25934             var r = this.doc.selection.createRange();
25935             if(r){
25936                 r.collapse(true);
25937                 r.pasteHTML(text);
25938                 this.syncValue();
25939                 this.deferFocus();
25940             
25941             }
25942             return;
25943         }
25944         */
25945         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25946             this.win.focus();
25947             
25948             
25949             // from jquery ui (MIT licenced)
25950             var range, node;
25951             var win = this.win;
25952             
25953             if (win.getSelection && win.getSelection().getRangeAt) {
25954                 range = win.getSelection().getRangeAt(0);
25955                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25956                 range.insertNode(node);
25957             } else if (win.document.selection && win.document.selection.createRange) {
25958                 // no firefox support
25959                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25960                 win.document.selection.createRange().pasteHTML(txt);
25961             } else {
25962                 // no firefox support
25963                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25964                 this.execCmd('InsertHTML', txt);
25965             } 
25966             
25967             this.syncValue();
25968             
25969             this.deferFocus();
25970         }
25971     },
25972  // private
25973     mozKeyPress : function(e){
25974         if(e.ctrlKey){
25975             var c = e.getCharCode(), cmd;
25976           
25977             if(c > 0){
25978                 c = String.fromCharCode(c).toLowerCase();
25979                 switch(c){
25980                     case 'b':
25981                         cmd = 'bold';
25982                         break;
25983                     case 'i':
25984                         cmd = 'italic';
25985                         break;
25986                     
25987                     case 'u':
25988                         cmd = 'underline';
25989                         break;
25990                     
25991                     case 'v':
25992                         this.cleanUpPaste.defer(100, this);
25993                         return;
25994                         
25995                 }
25996                 if(cmd){
25997                     this.win.focus();
25998                     this.execCmd(cmd);
25999                     this.deferFocus();
26000                     e.preventDefault();
26001                 }
26002                 
26003             }
26004         }
26005     },
26006
26007     // private
26008     fixKeys : function(){ // load time branching for fastest keydown performance
26009         if(Roo.isIE){
26010             return function(e){
26011                 var k = e.getKey(), r;
26012                 if(k == e.TAB){
26013                     e.stopEvent();
26014                     r = this.doc.selection.createRange();
26015                     if(r){
26016                         r.collapse(true);
26017                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26018                         this.deferFocus();
26019                     }
26020                     return;
26021                 }
26022                 
26023                 if(k == e.ENTER){
26024                     r = this.doc.selection.createRange();
26025                     if(r){
26026                         var target = r.parentElement();
26027                         if(!target || target.tagName.toLowerCase() != 'li'){
26028                             e.stopEvent();
26029                             r.pasteHTML('<br />');
26030                             r.collapse(false);
26031                             r.select();
26032                         }
26033                     }
26034                 }
26035                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26036                     this.cleanUpPaste.defer(100, this);
26037                     return;
26038                 }
26039                 
26040                 
26041             };
26042         }else if(Roo.isOpera){
26043             return function(e){
26044                 var k = e.getKey();
26045                 if(k == e.TAB){
26046                     e.stopEvent();
26047                     this.win.focus();
26048                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26049                     this.deferFocus();
26050                 }
26051                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26052                     this.cleanUpPaste.defer(100, this);
26053                     return;
26054                 }
26055                 
26056             };
26057         }else if(Roo.isSafari){
26058             return function(e){
26059                 var k = e.getKey();
26060                 
26061                 if(k == e.TAB){
26062                     e.stopEvent();
26063                     this.execCmd('InsertText','\t');
26064                     this.deferFocus();
26065                     return;
26066                 }
26067                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26068                     this.cleanUpPaste.defer(100, this);
26069                     return;
26070                 }
26071                 
26072              };
26073         }
26074     }(),
26075     
26076     getAllAncestors: function()
26077     {
26078         var p = this.getSelectedNode();
26079         var a = [];
26080         if (!p) {
26081             a.push(p); // push blank onto stack..
26082             p = this.getParentElement();
26083         }
26084         
26085         
26086         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26087             a.push(p);
26088             p = p.parentNode;
26089         }
26090         a.push(this.doc.body);
26091         return a;
26092     },
26093     lastSel : false,
26094     lastSelNode : false,
26095     
26096     
26097     getSelection : function() 
26098     {
26099         this.assignDocWin();
26100         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26101     },
26102     
26103     getSelectedNode: function() 
26104     {
26105         // this may only work on Gecko!!!
26106         
26107         // should we cache this!!!!
26108         
26109         
26110         
26111          
26112         var range = this.createRange(this.getSelection()).cloneRange();
26113         
26114         if (Roo.isIE) {
26115             var parent = range.parentElement();
26116             while (true) {
26117                 var testRange = range.duplicate();
26118                 testRange.moveToElementText(parent);
26119                 if (testRange.inRange(range)) {
26120                     break;
26121                 }
26122                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26123                     break;
26124                 }
26125                 parent = parent.parentElement;
26126             }
26127             return parent;
26128         }
26129         
26130         // is ancestor a text element.
26131         var ac =  range.commonAncestorContainer;
26132         if (ac.nodeType == 3) {
26133             ac = ac.parentNode;
26134         }
26135         
26136         var ar = ac.childNodes;
26137          
26138         var nodes = [];
26139         var other_nodes = [];
26140         var has_other_nodes = false;
26141         for (var i=0;i<ar.length;i++) {
26142             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26143                 continue;
26144             }
26145             // fullly contained node.
26146             
26147             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26148                 nodes.push(ar[i]);
26149                 continue;
26150             }
26151             
26152             // probably selected..
26153             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26154                 other_nodes.push(ar[i]);
26155                 continue;
26156             }
26157             // outer..
26158             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26159                 continue;
26160             }
26161             
26162             
26163             has_other_nodes = true;
26164         }
26165         if (!nodes.length && other_nodes.length) {
26166             nodes= other_nodes;
26167         }
26168         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26169             return false;
26170         }
26171         
26172         return nodes[0];
26173     },
26174     createRange: function(sel)
26175     {
26176         // this has strange effects when using with 
26177         // top toolbar - not sure if it's a great idea.
26178         //this.editor.contentWindow.focus();
26179         if (typeof sel != "undefined") {
26180             try {
26181                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26182             } catch(e) {
26183                 return this.doc.createRange();
26184             }
26185         } else {
26186             return this.doc.createRange();
26187         }
26188     },
26189     getParentElement: function()
26190     {
26191         
26192         this.assignDocWin();
26193         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26194         
26195         var range = this.createRange(sel);
26196          
26197         try {
26198             var p = range.commonAncestorContainer;
26199             while (p.nodeType == 3) { // text node
26200                 p = p.parentNode;
26201             }
26202             return p;
26203         } catch (e) {
26204             return null;
26205         }
26206     
26207     },
26208     /***
26209      *
26210      * Range intersection.. the hard stuff...
26211      *  '-1' = before
26212      *  '0' = hits..
26213      *  '1' = after.
26214      *         [ -- selected range --- ]
26215      *   [fail]                        [fail]
26216      *
26217      *    basically..
26218      *      if end is before start or  hits it. fail.
26219      *      if start is after end or hits it fail.
26220      *
26221      *   if either hits (but other is outside. - then it's not 
26222      *   
26223      *    
26224      **/
26225     
26226     
26227     // @see http://www.thismuchiknow.co.uk/?p=64.
26228     rangeIntersectsNode : function(range, node)
26229     {
26230         var nodeRange = node.ownerDocument.createRange();
26231         try {
26232             nodeRange.selectNode(node);
26233         } catch (e) {
26234             nodeRange.selectNodeContents(node);
26235         }
26236     
26237         var rangeStartRange = range.cloneRange();
26238         rangeStartRange.collapse(true);
26239     
26240         var rangeEndRange = range.cloneRange();
26241         rangeEndRange.collapse(false);
26242     
26243         var nodeStartRange = nodeRange.cloneRange();
26244         nodeStartRange.collapse(true);
26245     
26246         var nodeEndRange = nodeRange.cloneRange();
26247         nodeEndRange.collapse(false);
26248     
26249         return rangeStartRange.compareBoundaryPoints(
26250                  Range.START_TO_START, nodeEndRange) == -1 &&
26251                rangeEndRange.compareBoundaryPoints(
26252                  Range.START_TO_START, nodeStartRange) == 1;
26253         
26254          
26255     },
26256     rangeCompareNode : function(range, node)
26257     {
26258         var nodeRange = node.ownerDocument.createRange();
26259         try {
26260             nodeRange.selectNode(node);
26261         } catch (e) {
26262             nodeRange.selectNodeContents(node);
26263         }
26264         
26265         
26266         range.collapse(true);
26267     
26268         nodeRange.collapse(true);
26269      
26270         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26271         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26272          
26273         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26274         
26275         var nodeIsBefore   =  ss == 1;
26276         var nodeIsAfter    = ee == -1;
26277         
26278         if (nodeIsBefore && nodeIsAfter)
26279             return 0; // outer
26280         if (!nodeIsBefore && nodeIsAfter)
26281             return 1; //right trailed.
26282         
26283         if (nodeIsBefore && !nodeIsAfter)
26284             return 2;  // left trailed.
26285         // fully contined.
26286         return 3;
26287     },
26288
26289     // private? - in a new class?
26290     cleanUpPaste :  function()
26291     {
26292         // cleans up the whole document..
26293          Roo.log('cleanuppaste');
26294         this.cleanUpChildren(this.doc.body);
26295         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26296         if (clean != this.doc.body.innerHTML) {
26297             this.doc.body.innerHTML = clean;
26298         }
26299         
26300     },
26301     
26302     cleanWordChars : function(input) {// change the chars to hex code
26303         var he = Roo.form.HtmlEditor;
26304         
26305         var output = input;
26306         Roo.each(he.swapCodes, function(sw) { 
26307             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26308             
26309             output = output.replace(swapper, sw[1]);
26310         });
26311         
26312         return output;
26313     },
26314     
26315     
26316     cleanUpChildren : function (n)
26317     {
26318         if (!n.childNodes.length) {
26319             return;
26320         }
26321         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26322            this.cleanUpChild(n.childNodes[i]);
26323         }
26324     },
26325     
26326     
26327         
26328     
26329     cleanUpChild : function (node)
26330     {
26331         var ed = this;
26332         //console.log(node);
26333         if (node.nodeName == "#text") {
26334             // clean up silly Windows -- stuff?
26335             return; 
26336         }
26337         if (node.nodeName == "#comment") {
26338             node.parentNode.removeChild(node);
26339             // clean up silly Windows -- stuff?
26340             return; 
26341         }
26342         
26343         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26344             // remove node.
26345             node.parentNode.removeChild(node);
26346             return;
26347             
26348         }
26349         
26350         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26351         
26352         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26353         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26354         
26355         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26356         //    remove_keep_children = true;
26357         //}
26358         
26359         if (remove_keep_children) {
26360             this.cleanUpChildren(node);
26361             // inserts everything just before this node...
26362             while (node.childNodes.length) {
26363                 var cn = node.childNodes[0];
26364                 node.removeChild(cn);
26365                 node.parentNode.insertBefore(cn, node);
26366             }
26367             node.parentNode.removeChild(node);
26368             return;
26369         }
26370         
26371         if (!node.attributes || !node.attributes.length) {
26372             this.cleanUpChildren(node);
26373             return;
26374         }
26375         
26376         function cleanAttr(n,v)
26377         {
26378             
26379             if (v.match(/^\./) || v.match(/^\//)) {
26380                 return;
26381             }
26382             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26383                 return;
26384             }
26385             if (v.match(/^#/)) {
26386                 return;
26387             }
26388 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26389             node.removeAttribute(n);
26390             
26391         }
26392         
26393         function cleanStyle(n,v)
26394         {
26395             if (v.match(/expression/)) { //XSS?? should we even bother..
26396                 node.removeAttribute(n);
26397                 return;
26398             }
26399             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
26400             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
26401             
26402             
26403             var parts = v.split(/;/);
26404             var clean = [];
26405             
26406             Roo.each(parts, function(p) {
26407                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26408                 if (!p.length) {
26409                     return true;
26410                 }
26411                 var l = p.split(':').shift().replace(/\s+/g,'');
26412                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26413                 
26414                 
26415                 if ( cblack.indexOf(l) > -1) {
26416 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26417                     //node.removeAttribute(n);
26418                     return true;
26419                 }
26420                 //Roo.log()
26421                 // only allow 'c whitelisted system attributes'
26422                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26423 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26424                     //node.removeAttribute(n);
26425                     return true;
26426                 }
26427                 
26428                 
26429                  
26430                 
26431                 clean.push(p);
26432                 return true;
26433             });
26434             if (clean.length) { 
26435                 node.setAttribute(n, clean.join(';'));
26436             } else {
26437                 node.removeAttribute(n);
26438             }
26439             
26440         }
26441         
26442         
26443         for (var i = node.attributes.length-1; i > -1 ; i--) {
26444             var a = node.attributes[i];
26445             //console.log(a);
26446             
26447             if (a.name.toLowerCase().substr(0,2)=='on')  {
26448                 node.removeAttribute(a.name);
26449                 continue;
26450             }
26451             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26452                 node.removeAttribute(a.name);
26453                 continue;
26454             }
26455             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26456                 cleanAttr(a.name,a.value); // fixme..
26457                 continue;
26458             }
26459             if (a.name == 'style') {
26460                 cleanStyle(a.name,a.value);
26461                 continue;
26462             }
26463             /// clean up MS crap..
26464             // tecnically this should be a list of valid class'es..
26465             
26466             
26467             if (a.name == 'class') {
26468                 if (a.value.match(/^Mso/)) {
26469                     node.className = '';
26470                 }
26471                 
26472                 if (a.value.match(/body/)) {
26473                     node.className = '';
26474                 }
26475                 continue;
26476             }
26477             
26478             // style cleanup!?
26479             // class cleanup?
26480             
26481         }
26482         
26483         
26484         this.cleanUpChildren(node);
26485         
26486         
26487     }
26488     
26489     
26490     // hide stuff that is not compatible
26491     /**
26492      * @event blur
26493      * @hide
26494      */
26495     /**
26496      * @event change
26497      * @hide
26498      */
26499     /**
26500      * @event focus
26501      * @hide
26502      */
26503     /**
26504      * @event specialkey
26505      * @hide
26506      */
26507     /**
26508      * @cfg {String} fieldClass @hide
26509      */
26510     /**
26511      * @cfg {String} focusClass @hide
26512      */
26513     /**
26514      * @cfg {String} autoCreate @hide
26515      */
26516     /**
26517      * @cfg {String} inputType @hide
26518      */
26519     /**
26520      * @cfg {String} invalidClass @hide
26521      */
26522     /**
26523      * @cfg {String} invalidText @hide
26524      */
26525     /**
26526      * @cfg {String} msgFx @hide
26527      */
26528     /**
26529      * @cfg {String} validateOnBlur @hide
26530      */
26531 });
26532
26533 Roo.form.HtmlEditor.white = [
26534         'area', 'br', 'img', 'input', 'hr', 'wbr',
26535         
26536        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26537        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26538        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26539        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26540        'table',   'ul',         'xmp', 
26541        
26542        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26543       'thead',   'tr', 
26544      
26545       'dir', 'menu', 'ol', 'ul', 'dl',
26546        
26547       'embed',  'object'
26548 ];
26549
26550
26551 Roo.form.HtmlEditor.black = [
26552     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26553         'applet', // 
26554         'base',   'basefont', 'bgsound', 'blink',  'body', 
26555         'frame',  'frameset', 'head',    'html',   'ilayer', 
26556         'iframe', 'layer',  'link',     'meta',    'object',   
26557         'script', 'style' ,'title',  'xml' // clean later..
26558 ];
26559 Roo.form.HtmlEditor.clean = [
26560     'script', 'style', 'title', 'xml'
26561 ];
26562 Roo.form.HtmlEditor.remove = [
26563     'font'
26564 ];
26565 // attributes..
26566
26567 Roo.form.HtmlEditor.ablack = [
26568     'on'
26569 ];
26570     
26571 Roo.form.HtmlEditor.aclean = [ 
26572     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26573 ];
26574
26575 // protocols..
26576 Roo.form.HtmlEditor.pwhite= [
26577         'http',  'https',  'mailto'
26578 ];
26579
26580 // white listed style attributes.
26581 Roo.form.HtmlEditor.cwhite= [
26582       //  'text-align', /// default is to allow most things..
26583       
26584          
26585 //        'font-size'//??
26586 ];
26587
26588 // black listed style attributes.
26589 Roo.form.HtmlEditor.cblack= [
26590       //  'font-size' -- this can be set by the project 
26591 ];
26592
26593
26594 Roo.form.HtmlEditor.swapCodes   =[ 
26595     [    8211, "--" ], 
26596     [    8212, "--" ], 
26597     [    8216,  "'" ],  
26598     [    8217, "'" ],  
26599     [    8220, '"' ],  
26600     [    8221, '"' ],  
26601     [    8226, "*" ],  
26602     [    8230, "..." ]
26603 ]; 
26604
26605     // <script type="text/javascript">
26606 /*
26607  * Based on
26608  * Ext JS Library 1.1.1
26609  * Copyright(c) 2006-2007, Ext JS, LLC.
26610  *  
26611  
26612  */
26613
26614 /**
26615  * @class Roo.form.HtmlEditorToolbar1
26616  * Basic Toolbar
26617  * 
26618  * Usage:
26619  *
26620  new Roo.form.HtmlEditor({
26621     ....
26622     toolbars : [
26623         new Roo.form.HtmlEditorToolbar1({
26624             disable : { fonts: 1 , format: 1, ..., ... , ...],
26625             btns : [ .... ]
26626         })
26627     }
26628      
26629  * 
26630  * @cfg {Object} disable List of elements to disable..
26631  * @cfg {Array} btns List of additional buttons.
26632  * 
26633  * 
26634  * NEEDS Extra CSS? 
26635  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26636  */
26637  
26638 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26639 {
26640     
26641     Roo.apply(this, config);
26642     
26643     // default disabled, based on 'good practice'..
26644     this.disable = this.disable || {};
26645     Roo.applyIf(this.disable, {
26646         fontSize : true,
26647         colors : true,
26648         specialElements : true
26649     });
26650     
26651     
26652     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26653     // dont call parent... till later.
26654 }
26655
26656 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26657     
26658     tb: false,
26659     
26660     rendered: false,
26661     
26662     editor : false,
26663     /**
26664      * @cfg {Object} disable  List of toolbar elements to disable
26665          
26666      */
26667     disable : false,
26668       /**
26669      * @cfg {Array} fontFamilies An array of available font families
26670      */
26671     fontFamilies : [
26672         'Arial',
26673         'Courier New',
26674         'Tahoma',
26675         'Times New Roman',
26676         'Verdana'
26677     ],
26678     
26679     specialChars : [
26680            "&#169;",
26681           "&#174;",     
26682           "&#8482;",    
26683           "&#163;" ,    
26684          // "&#8212;",    
26685           "&#8230;",    
26686           "&#247;" ,    
26687         //  "&#225;" ,     ?? a acute?
26688            "&#8364;"    , //Euro
26689        //   "&#8220;"    ,
26690         //  "&#8221;"    ,
26691         //  "&#8226;"    ,
26692           "&#176;"  //   , // degrees
26693
26694          // "&#233;"     , // e ecute
26695          // "&#250;"     , // u ecute?
26696     ],
26697     
26698     specialElements : [
26699         {
26700             text: "Insert Table",
26701             xtype: 'MenuItem',
26702             xns : Roo.Menu,
26703             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26704                 
26705         },
26706         {    
26707             text: "Insert Image",
26708             xtype: 'MenuItem',
26709             xns : Roo.Menu,
26710             ihtml : '<img src="about:blank"/>'
26711             
26712         }
26713         
26714          
26715     ],
26716     
26717     
26718     inputElements : [ 
26719             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26720             "input:submit", "input:button", "select", "textarea", "label" ],
26721     formats : [
26722         ["p"] ,  
26723         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26724         ["pre"],[ "code"], 
26725         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26726         ['div'],['span']
26727     ],
26728      /**
26729      * @cfg {String} defaultFont default font to use.
26730      */
26731     defaultFont: 'tahoma',
26732    
26733     fontSelect : false,
26734     
26735     
26736     formatCombo : false,
26737     
26738     init : function(editor)
26739     {
26740         this.editor = editor;
26741         
26742         
26743         var fid = editor.frameId;
26744         var etb = this;
26745         function btn(id, toggle, handler){
26746             var xid = fid + '-'+ id ;
26747             return {
26748                 id : xid,
26749                 cmd : id,
26750                 cls : 'x-btn-icon x-edit-'+id,
26751                 enableToggle:toggle !== false,
26752                 scope: editor, // was editor...
26753                 handler:handler||editor.relayBtnCmd,
26754                 clickEvent:'mousedown',
26755                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26756                 tabIndex:-1
26757             };
26758         }
26759         
26760         
26761         
26762         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26763         this.tb = tb;
26764          // stop form submits
26765         tb.el.on('click', function(e){
26766             e.preventDefault(); // what does this do?
26767         });
26768
26769         if(!this.disable.font) { // && !Roo.isSafari){
26770             /* why no safari for fonts 
26771             editor.fontSelect = tb.el.createChild({
26772                 tag:'select',
26773                 tabIndex: -1,
26774                 cls:'x-font-select',
26775                 html: this.createFontOptions()
26776             });
26777             
26778             editor.fontSelect.on('change', function(){
26779                 var font = editor.fontSelect.dom.value;
26780                 editor.relayCmd('fontname', font);
26781                 editor.deferFocus();
26782             }, editor);
26783             
26784             tb.add(
26785                 editor.fontSelect.dom,
26786                 '-'
26787             );
26788             */
26789             
26790         };
26791         if(!this.disable.formats){
26792             this.formatCombo = new Roo.form.ComboBox({
26793                 store: new Roo.data.SimpleStore({
26794                     id : 'tag',
26795                     fields: ['tag'],
26796                     data : this.formats // from states.js
26797                 }),
26798                 blockFocus : true,
26799                 name : '',
26800                 //autoCreate : {tag: "div",  size: "20"},
26801                 displayField:'tag',
26802                 typeAhead: false,
26803                 mode: 'local',
26804                 editable : false,
26805                 triggerAction: 'all',
26806                 emptyText:'Add tag',
26807                 selectOnFocus:true,
26808                 width:135,
26809                 listeners : {
26810                     'select': function(c, r, i) {
26811                         editor.insertTag(r.get('tag'));
26812                         editor.focus();
26813                     }
26814                 }
26815
26816             });
26817             tb.addField(this.formatCombo);
26818             
26819         }
26820         
26821         if(!this.disable.format){
26822             tb.add(
26823                 btn('bold'),
26824                 btn('italic'),
26825                 btn('underline')
26826             );
26827         };
26828         if(!this.disable.fontSize){
26829             tb.add(
26830                 '-',
26831                 
26832                 
26833                 btn('increasefontsize', false, editor.adjustFont),
26834                 btn('decreasefontsize', false, editor.adjustFont)
26835             );
26836         };
26837         
26838         
26839         if(!this.disable.colors){
26840             tb.add(
26841                 '-', {
26842                     id:editor.frameId +'-forecolor',
26843                     cls:'x-btn-icon x-edit-forecolor',
26844                     clickEvent:'mousedown',
26845                     tooltip: this.buttonTips['forecolor'] || undefined,
26846                     tabIndex:-1,
26847                     menu : new Roo.menu.ColorMenu({
26848                         allowReselect: true,
26849                         focus: Roo.emptyFn,
26850                         value:'000000',
26851                         plain:true,
26852                         selectHandler: function(cp, color){
26853                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26854                             editor.deferFocus();
26855                         },
26856                         scope: editor,
26857                         clickEvent:'mousedown'
26858                     })
26859                 }, {
26860                     id:editor.frameId +'backcolor',
26861                     cls:'x-btn-icon x-edit-backcolor',
26862                     clickEvent:'mousedown',
26863                     tooltip: this.buttonTips['backcolor'] || undefined,
26864                     tabIndex:-1,
26865                     menu : new Roo.menu.ColorMenu({
26866                         focus: Roo.emptyFn,
26867                         value:'FFFFFF',
26868                         plain:true,
26869                         allowReselect: true,
26870                         selectHandler: function(cp, color){
26871                             if(Roo.isGecko){
26872                                 editor.execCmd('useCSS', false);
26873                                 editor.execCmd('hilitecolor', color);
26874                                 editor.execCmd('useCSS', true);
26875                                 editor.deferFocus();
26876                             }else{
26877                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26878                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26879                                 editor.deferFocus();
26880                             }
26881                         },
26882                         scope:editor,
26883                         clickEvent:'mousedown'
26884                     })
26885                 }
26886             );
26887         };
26888         // now add all the items...
26889         
26890
26891         if(!this.disable.alignments){
26892             tb.add(
26893                 '-',
26894                 btn('justifyleft'),
26895                 btn('justifycenter'),
26896                 btn('justifyright')
26897             );
26898         };
26899
26900         //if(!Roo.isSafari){
26901             if(!this.disable.links){
26902                 tb.add(
26903                     '-',
26904                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26905                 );
26906             };
26907
26908             if(!this.disable.lists){
26909                 tb.add(
26910                     '-',
26911                     btn('insertorderedlist'),
26912                     btn('insertunorderedlist')
26913                 );
26914             }
26915             if(!this.disable.sourceEdit){
26916                 tb.add(
26917                     '-',
26918                     btn('sourceedit', true, function(btn){
26919                         this.toggleSourceEdit(btn.pressed);
26920                     })
26921                 );
26922             }
26923         //}
26924         
26925         var smenu = { };
26926         // special menu.. - needs to be tidied up..
26927         if (!this.disable.special) {
26928             smenu = {
26929                 text: "&#169;",
26930                 cls: 'x-edit-none',
26931                 
26932                 menu : {
26933                     items : []
26934                 }
26935             };
26936             for (var i =0; i < this.specialChars.length; i++) {
26937                 smenu.menu.items.push({
26938                     
26939                     html: this.specialChars[i],
26940                     handler: function(a,b) {
26941                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26942                         //editor.insertAtCursor(a.html);
26943                         
26944                     },
26945                     tabIndex:-1
26946                 });
26947             }
26948             
26949             
26950             tb.add(smenu);
26951             
26952             
26953         }
26954          
26955         if (!this.disable.specialElements) {
26956             var semenu = {
26957                 text: "Other;",
26958                 cls: 'x-edit-none',
26959                 menu : {
26960                     items : []
26961                 }
26962             };
26963             for (var i =0; i < this.specialElements.length; i++) {
26964                 semenu.menu.items.push(
26965                     Roo.apply({ 
26966                         handler: function(a,b) {
26967                             editor.insertAtCursor(this.ihtml);
26968                         }
26969                     }, this.specialElements[i])
26970                 );
26971                     
26972             }
26973             
26974             tb.add(semenu);
26975             
26976             
26977         }
26978          
26979         
26980         if (this.btns) {
26981             for(var i =0; i< this.btns.length;i++) {
26982                 var b = Roo.factory(this.btns[i],Roo.form);
26983                 b.cls =  'x-edit-none';
26984                 b.scope = editor;
26985                 tb.add(b);
26986             }
26987         
26988         }
26989         
26990         
26991         
26992         // disable everything...
26993         
26994         this.tb.items.each(function(item){
26995            if(item.id != editor.frameId+ '-sourceedit'){
26996                 item.disable();
26997             }
26998         });
26999         this.rendered = true;
27000         
27001         // the all the btns;
27002         editor.on('editorevent', this.updateToolbar, this);
27003         // other toolbars need to implement this..
27004         //editor.on('editmodechange', this.updateToolbar, this);
27005     },
27006     
27007     
27008     
27009     /**
27010      * Protected method that will not generally be called directly. It triggers
27011      * a toolbar update by reading the markup state of the current selection in the editor.
27012      */
27013     updateToolbar: function(){
27014
27015         if(!this.editor.activated){
27016             this.editor.onFirstFocus();
27017             return;
27018         }
27019
27020         var btns = this.tb.items.map, 
27021             doc = this.editor.doc,
27022             frameId = this.editor.frameId;
27023
27024         if(!this.disable.font && !Roo.isSafari){
27025             /*
27026             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27027             if(name != this.fontSelect.dom.value){
27028                 this.fontSelect.dom.value = name;
27029             }
27030             */
27031         }
27032         if(!this.disable.format){
27033             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27034             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27035             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27036         }
27037         if(!this.disable.alignments){
27038             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27039             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27040             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27041         }
27042         if(!Roo.isSafari && !this.disable.lists){
27043             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27044             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27045         }
27046         
27047         var ans = this.editor.getAllAncestors();
27048         if (this.formatCombo) {
27049             
27050             
27051             var store = this.formatCombo.store;
27052             this.formatCombo.setValue("");
27053             for (var i =0; i < ans.length;i++) {
27054                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27055                     // select it..
27056                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27057                     break;
27058                 }
27059             }
27060         }
27061         
27062         
27063         
27064         // hides menus... - so this cant be on a menu...
27065         Roo.menu.MenuMgr.hideAll();
27066
27067         //this.editorsyncValue();
27068     },
27069    
27070     
27071     createFontOptions : function(){
27072         var buf = [], fs = this.fontFamilies, ff, lc;
27073         
27074         
27075         
27076         for(var i = 0, len = fs.length; i< len; i++){
27077             ff = fs[i];
27078             lc = ff.toLowerCase();
27079             buf.push(
27080                 '<option value="',lc,'" style="font-family:',ff,';"',
27081                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27082                     ff,
27083                 '</option>'
27084             );
27085         }
27086         return buf.join('');
27087     },
27088     
27089     toggleSourceEdit : function(sourceEditMode){
27090         if(sourceEditMode === undefined){
27091             sourceEditMode = !this.sourceEditMode;
27092         }
27093         this.sourceEditMode = sourceEditMode === true;
27094         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
27095         // just toggle the button?
27096         if(btn.pressed !== this.editor.sourceEditMode){
27097             btn.toggle(this.editor.sourceEditMode);
27098             return;
27099         }
27100         
27101         if(this.sourceEditMode){
27102             this.tb.items.each(function(item){
27103                 if(item.cmd != 'sourceedit'){
27104                     item.disable();
27105                 }
27106             });
27107           
27108         }else{
27109             if(this.initialized){
27110                 this.tb.items.each(function(item){
27111                     item.enable();
27112                 });
27113             }
27114             
27115         }
27116         // tell the editor that it's been pressed..
27117         this.editor.toggleSourceEdit(sourceEditMode);
27118        
27119     },
27120      /**
27121      * Object collection of toolbar tooltips for the buttons in the editor. The key
27122      * is the command id associated with that button and the value is a valid QuickTips object.
27123      * For example:
27124 <pre><code>
27125 {
27126     bold : {
27127         title: 'Bold (Ctrl+B)',
27128         text: 'Make the selected text bold.',
27129         cls: 'x-html-editor-tip'
27130     },
27131     italic : {
27132         title: 'Italic (Ctrl+I)',
27133         text: 'Make the selected text italic.',
27134         cls: 'x-html-editor-tip'
27135     },
27136     ...
27137 </code></pre>
27138     * @type Object
27139      */
27140     buttonTips : {
27141         bold : {
27142             title: 'Bold (Ctrl+B)',
27143             text: 'Make the selected text bold.',
27144             cls: 'x-html-editor-tip'
27145         },
27146         italic : {
27147             title: 'Italic (Ctrl+I)',
27148             text: 'Make the selected text italic.',
27149             cls: 'x-html-editor-tip'
27150         },
27151         underline : {
27152             title: 'Underline (Ctrl+U)',
27153             text: 'Underline the selected text.',
27154             cls: 'x-html-editor-tip'
27155         },
27156         increasefontsize : {
27157             title: 'Grow Text',
27158             text: 'Increase the font size.',
27159             cls: 'x-html-editor-tip'
27160         },
27161         decreasefontsize : {
27162             title: 'Shrink Text',
27163             text: 'Decrease the font size.',
27164             cls: 'x-html-editor-tip'
27165         },
27166         backcolor : {
27167             title: 'Text Highlight Color',
27168             text: 'Change the background color of the selected text.',
27169             cls: 'x-html-editor-tip'
27170         },
27171         forecolor : {
27172             title: 'Font Color',
27173             text: 'Change the color of the selected text.',
27174             cls: 'x-html-editor-tip'
27175         },
27176         justifyleft : {
27177             title: 'Align Text Left',
27178             text: 'Align text to the left.',
27179             cls: 'x-html-editor-tip'
27180         },
27181         justifycenter : {
27182             title: 'Center Text',
27183             text: 'Center text in the editor.',
27184             cls: 'x-html-editor-tip'
27185         },
27186         justifyright : {
27187             title: 'Align Text Right',
27188             text: 'Align text to the right.',
27189             cls: 'x-html-editor-tip'
27190         },
27191         insertunorderedlist : {
27192             title: 'Bullet List',
27193             text: 'Start a bulleted list.',
27194             cls: 'x-html-editor-tip'
27195         },
27196         insertorderedlist : {
27197             title: 'Numbered List',
27198             text: 'Start a numbered list.',
27199             cls: 'x-html-editor-tip'
27200         },
27201         createlink : {
27202             title: 'Hyperlink',
27203             text: 'Make the selected text a hyperlink.',
27204             cls: 'x-html-editor-tip'
27205         },
27206         sourceedit : {
27207             title: 'Source Edit',
27208             text: 'Switch to source editing mode.',
27209             cls: 'x-html-editor-tip'
27210         }
27211     },
27212     // private
27213     onDestroy : function(){
27214         if(this.rendered){
27215             
27216             this.tb.items.each(function(item){
27217                 if(item.menu){
27218                     item.menu.removeAll();
27219                     if(item.menu.el){
27220                         item.menu.el.destroy();
27221                     }
27222                 }
27223                 item.destroy();
27224             });
27225              
27226         }
27227     },
27228     onFirstFocus: function() {
27229         this.tb.items.each(function(item){
27230            item.enable();
27231         });
27232     }
27233 });
27234
27235
27236
27237
27238 // <script type="text/javascript">
27239 /*
27240  * Based on
27241  * Ext JS Library 1.1.1
27242  * Copyright(c) 2006-2007, Ext JS, LLC.
27243  *  
27244  
27245  */
27246
27247  
27248 /**
27249  * @class Roo.form.HtmlEditor.ToolbarContext
27250  * Context Toolbar
27251  * 
27252  * Usage:
27253  *
27254  new Roo.form.HtmlEditor({
27255     ....
27256     toolbars : [
27257         { xtype: 'ToolbarStandard', styles : {} }
27258         { xtype: 'ToolbarContext', disable : {} }
27259     ]
27260 })
27261
27262      
27263  * 
27264  * @config : {Object} disable List of elements to disable.. (not done yet.)
27265  * @config : {Object} styles  Map of styles available.
27266  * 
27267  */
27268
27269 Roo.form.HtmlEditor.ToolbarContext = function(config)
27270 {
27271     
27272     Roo.apply(this, config);
27273     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27274     // dont call parent... till later.
27275     this.styles = this.styles || {};
27276 }
27277
27278  
27279
27280 Roo.form.HtmlEditor.ToolbarContext.types = {
27281     'IMG' : {
27282         width : {
27283             title: "Width",
27284             width: 40
27285         },
27286         height:  {
27287             title: "Height",
27288             width: 40
27289         },
27290         align: {
27291             title: "Align",
27292             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27293             width : 80
27294             
27295         },
27296         border: {
27297             title: "Border",
27298             width: 40
27299         },
27300         alt: {
27301             title: "Alt",
27302             width: 120
27303         },
27304         src : {
27305             title: "Src",
27306             width: 220
27307         }
27308         
27309     },
27310     'A' : {
27311         name : {
27312             title: "Name",
27313             width: 50
27314         },
27315         href:  {
27316             title: "Href",
27317             width: 220
27318         } // border?
27319         
27320     },
27321     'TABLE' : {
27322         rows : {
27323             title: "Rows",
27324             width: 20
27325         },
27326         cols : {
27327             title: "Cols",
27328             width: 20
27329         },
27330         width : {
27331             title: "Width",
27332             width: 40
27333         },
27334         height : {
27335             title: "Height",
27336             width: 40
27337         },
27338         border : {
27339             title: "Border",
27340             width: 20
27341         }
27342     },
27343     'TD' : {
27344         width : {
27345             title: "Width",
27346             width: 40
27347         },
27348         height : {
27349             title: "Height",
27350             width: 40
27351         },   
27352         align: {
27353             title: "Align",
27354             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27355             width: 80
27356         },
27357         valign: {
27358             title: "Valign",
27359             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27360             width: 80
27361         },
27362         colspan: {
27363             title: "Colspan",
27364             width: 20
27365             
27366         },
27367          'font-family'  : {
27368             title : "Font",
27369             style : 'fontFamily',
27370             displayField: 'display',
27371             optname : 'font-family',
27372             width: 140
27373         }
27374     },
27375     'INPUT' : {
27376         name : {
27377             title: "name",
27378             width: 120
27379         },
27380         value : {
27381             title: "Value",
27382             width: 120
27383         },
27384         width : {
27385             title: "Width",
27386             width: 40
27387         }
27388     },
27389     'LABEL' : {
27390         'for' : {
27391             title: "For",
27392             width: 120
27393         }
27394     },
27395     'TEXTAREA' : {
27396           name : {
27397             title: "name",
27398             width: 120
27399         },
27400         rows : {
27401             title: "Rows",
27402             width: 20
27403         },
27404         cols : {
27405             title: "Cols",
27406             width: 20
27407         }
27408     },
27409     'SELECT' : {
27410         name : {
27411             title: "name",
27412             width: 120
27413         },
27414         selectoptions : {
27415             title: "Options",
27416             width: 200
27417         }
27418     },
27419     
27420     // should we really allow this??
27421     // should this just be 
27422     'BODY' : {
27423         title : {
27424             title: "Title",
27425             width: 200,
27426             disabled : true
27427         }
27428     },
27429     'SPAN' : {
27430         'font-family'  : {
27431             title : "Font",
27432             style : 'fontFamily',
27433             displayField: 'display',
27434             optname : 'font-family',
27435             width: 140
27436         }
27437     },
27438     'DIV' : {
27439         'font-family'  : {
27440             title : "Font",
27441             style : 'fontFamily',
27442             displayField: 'display',
27443             optname : 'font-family',
27444             width: 140
27445         }
27446     },
27447      'P' : {
27448         'font-family'  : {
27449             title : "Font",
27450             style : 'fontFamily',
27451             displayField: 'display',
27452             optname : 'font-family',
27453             width: 140
27454         }
27455     },
27456     
27457     '*' : {
27458         // empty..
27459     }
27460
27461 };
27462
27463 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27464 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27465
27466 Roo.form.HtmlEditor.ToolbarContext.options = {
27467         'font-family'  : [ 
27468                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27469                 [ 'Courier New', 'Courier New'],
27470                 [ 'Tahoma', 'Tahoma'],
27471                 [ 'Times New Roman,serif', 'Times'],
27472                 [ 'Verdana','Verdana' ]
27473         ]
27474 };
27475
27476 // fixme - these need to be configurable..
27477  
27478
27479 Roo.form.HtmlEditor.ToolbarContext.types
27480
27481
27482 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27483     
27484     tb: false,
27485     
27486     rendered: false,
27487     
27488     editor : false,
27489     /**
27490      * @cfg {Object} disable  List of toolbar elements to disable
27491          
27492      */
27493     disable : false,
27494     /**
27495      * @cfg {Object} styles List of styles 
27496      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27497      *
27498      * These must be defined in the page, so they get rendered correctly..
27499      * .headline { }
27500      * TD.underline { }
27501      * 
27502      */
27503     styles : false,
27504     
27505     options: false,
27506     
27507     toolbars : false,
27508     
27509     init : function(editor)
27510     {
27511         this.editor = editor;
27512         
27513         
27514         var fid = editor.frameId;
27515         var etb = this;
27516         function btn(id, toggle, handler){
27517             var xid = fid + '-'+ id ;
27518             return {
27519                 id : xid,
27520                 cmd : id,
27521                 cls : 'x-btn-icon x-edit-'+id,
27522                 enableToggle:toggle !== false,
27523                 scope: editor, // was editor...
27524                 handler:handler||editor.relayBtnCmd,
27525                 clickEvent:'mousedown',
27526                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27527                 tabIndex:-1
27528             };
27529         }
27530         // create a new element.
27531         var wdiv = editor.wrap.createChild({
27532                 tag: 'div'
27533             }, editor.wrap.dom.firstChild.nextSibling, true);
27534         
27535         // can we do this more than once??
27536         
27537          // stop form submits
27538       
27539  
27540         // disable everything...
27541         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27542         this.toolbars = {};
27543            
27544         for (var i in  ty) {
27545           
27546             this.toolbars[i] = this.buildToolbar(ty[i],i);
27547         }
27548         this.tb = this.toolbars.BODY;
27549         this.tb.el.show();
27550         this.buildFooter();
27551         this.footer.show();
27552         editor.on('hide', function( ) { this.footer.hide() }, this);
27553         editor.on('show', function( ) { this.footer.show() }, this);
27554         
27555          
27556         this.rendered = true;
27557         
27558         // the all the btns;
27559         editor.on('editorevent', this.updateToolbar, this);
27560         // other toolbars need to implement this..
27561         //editor.on('editmodechange', this.updateToolbar, this);
27562     },
27563     
27564     
27565     
27566     /**
27567      * Protected method that will not generally be called directly. It triggers
27568      * a toolbar update by reading the markup state of the current selection in the editor.
27569      */
27570     updateToolbar: function(editor,ev,sel){
27571
27572         //Roo.log(ev);
27573         // capture mouse up - this is handy for selecting images..
27574         // perhaps should go somewhere else...
27575         if(!this.editor.activated){
27576              this.editor.onFirstFocus();
27577             return;
27578         }
27579         
27580         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27581         // selectNode - might want to handle IE?
27582         if (ev &&
27583             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27584             ev.target && ev.target.tagName == 'IMG') {
27585             // they have click on an image...
27586             // let's see if we can change the selection...
27587             sel = ev.target;
27588          
27589               var nodeRange = sel.ownerDocument.createRange();
27590             try {
27591                 nodeRange.selectNode(sel);
27592             } catch (e) {
27593                 nodeRange.selectNodeContents(sel);
27594             }
27595             //nodeRange.collapse(true);
27596             var s = editor.win.getSelection();
27597             s.removeAllRanges();
27598             s.addRange(nodeRange);
27599         }  
27600         
27601       
27602         var updateFooter = sel ? false : true;
27603         
27604         
27605         var ans = this.editor.getAllAncestors();
27606         
27607         // pick
27608         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27609         
27610         if (!sel) { 
27611             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
27612             sel = sel ? sel : this.editor.doc.body;
27613             sel = sel.tagName.length ? sel : this.editor.doc.body;
27614             
27615         }
27616         // pick a menu that exists..
27617         var tn = sel.tagName.toUpperCase();
27618         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27619         
27620         tn = sel.tagName.toUpperCase();
27621         
27622         var lastSel = this.tb.selectedNode
27623         
27624         this.tb.selectedNode = sel;
27625         
27626         // if current menu does not match..
27627         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27628                 
27629             this.tb.el.hide();
27630             ///console.log("show: " + tn);
27631             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27632             this.tb.el.show();
27633             // update name
27634             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27635             
27636             
27637             // update attributes
27638             if (this.tb.fields) {
27639                 this.tb.fields.each(function(e) {
27640                     if (e.stylename) {
27641                         e.setValue(sel.style[e.stylename]);
27642                         return;
27643                     } 
27644                    e.setValue(sel.getAttribute(e.attrname));
27645                 });
27646             }
27647             
27648             var hasStyles = false;
27649             for(var i in this.styles) {
27650                 hasStyles = true;
27651                 break;
27652             }
27653             
27654             // update styles
27655             if (hasStyles) { 
27656                 var st = this.tb.fields.item(0);
27657                 
27658                 st.store.removeAll();
27659                
27660                 
27661                 var cn = sel.className.split(/\s+/);
27662                 
27663                 var avs = [];
27664                 if (this.styles['*']) {
27665                     
27666                     Roo.each(this.styles['*'], function(v) {
27667                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27668                     });
27669                 }
27670                 if (this.styles[tn]) { 
27671                     Roo.each(this.styles[tn], function(v) {
27672                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27673                     });
27674                 }
27675                 
27676                 st.store.loadData(avs);
27677                 st.collapse();
27678                 st.setValue(cn);
27679             }
27680             // flag our selected Node.
27681             this.tb.selectedNode = sel;
27682            
27683            
27684             Roo.menu.MenuMgr.hideAll();
27685
27686         }
27687         
27688         if (!updateFooter) {
27689             //this.footDisp.dom.innerHTML = ''; 
27690             return;
27691         }
27692         // update the footer
27693         //
27694         var html = '';
27695         
27696         this.footerEls = ans.reverse();
27697         Roo.each(this.footerEls, function(a,i) {
27698             if (!a) { return; }
27699             html += html.length ? ' &gt; '  :  '';
27700             
27701             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27702             
27703         });
27704        
27705         // 
27706         var sz = this.footDisp.up('td').getSize();
27707         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27708         this.footDisp.dom.style.marginLeft = '5px';
27709         
27710         this.footDisp.dom.style.overflow = 'hidden';
27711         
27712         this.footDisp.dom.innerHTML = html;
27713             
27714         //this.editorsyncValue();
27715     },
27716      
27717     
27718    
27719        
27720     // private
27721     onDestroy : function(){
27722         if(this.rendered){
27723             
27724             this.tb.items.each(function(item){
27725                 if(item.menu){
27726                     item.menu.removeAll();
27727                     if(item.menu.el){
27728                         item.menu.el.destroy();
27729                     }
27730                 }
27731                 item.destroy();
27732             });
27733              
27734         }
27735     },
27736     onFirstFocus: function() {
27737         // need to do this for all the toolbars..
27738         this.tb.items.each(function(item){
27739            item.enable();
27740         });
27741     },
27742     buildToolbar: function(tlist, nm)
27743     {
27744         var editor = this.editor;
27745          // create a new element.
27746         var wdiv = editor.wrap.createChild({
27747                 tag: 'div'
27748             }, editor.wrap.dom.firstChild.nextSibling, true);
27749         
27750        
27751         var tb = new Roo.Toolbar(wdiv);
27752         // add the name..
27753         
27754         tb.add(nm+ ":&nbsp;");
27755         
27756         var styles = [];
27757         for(var i in this.styles) {
27758             styles.push(i);
27759         }
27760         
27761         // styles...
27762         if (styles && styles.length) {
27763             
27764             // this needs a multi-select checkbox...
27765             tb.addField( new Roo.form.ComboBox({
27766                 store: new Roo.data.SimpleStore({
27767                     id : 'val',
27768                     fields: ['val', 'selected'],
27769                     data : [] 
27770                 }),
27771                 name : '-roo-edit-className',
27772                 attrname : 'className',
27773                 displayField: 'val',
27774                 typeAhead: false,
27775                 mode: 'local',
27776                 editable : false,
27777                 triggerAction: 'all',
27778                 emptyText:'Select Style',
27779                 selectOnFocus:true,
27780                 width: 130,
27781                 listeners : {
27782                     'select': function(c, r, i) {
27783                         // initial support only for on class per el..
27784                         tb.selectedNode.className =  r ? r.get('val') : '';
27785                         editor.syncValue();
27786                     }
27787                 }
27788     
27789             }));
27790         }
27791         
27792         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27793         var tbops = tbc.options;
27794         
27795         for (var i in tlist) {
27796             
27797             var item = tlist[i];
27798             tb.add(item.title + ":&nbsp;");
27799             
27800             
27801             //optname == used so you can configure the options available..
27802             var opts = item.opts ? item.opts : false;
27803             if (item.optname) {
27804                 opts = tbops[item.optname];
27805            
27806             }
27807             
27808             if (opts) {
27809                 // opts == pulldown..
27810                 tb.addField( new Roo.form.ComboBox({
27811                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27812                         id : 'val',
27813                         fields: ['val', 'display'],
27814                         data : opts  
27815                     }),
27816                     name : '-roo-edit-' + i,
27817                     attrname : i,
27818                     stylename : item.style ? item.style : false,
27819                     displayField: item.displayField ? item.displayField : 'val',
27820                     valueField :  'val',
27821                     typeAhead: false,
27822                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27823                     editable : false,
27824                     triggerAction: 'all',
27825                     emptyText:'Select',
27826                     selectOnFocus:true,
27827                     width: item.width ? item.width  : 130,
27828                     listeners : {
27829                         'select': function(c, r, i) {
27830                             if (c.stylename) {
27831                                 tb.selectedNode.style[c.stylename] =  r.get('val');
27832                                 return;
27833                             }
27834                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27835                         }
27836                     }
27837
27838                 }));
27839                 continue;
27840                     
27841                  
27842                 
27843                 tb.addField( new Roo.form.TextField({
27844                     name: i,
27845                     width: 100,
27846                     //allowBlank:false,
27847                     value: ''
27848                 }));
27849                 continue;
27850             }
27851             tb.addField( new Roo.form.TextField({
27852                 name: '-roo-edit-' + i,
27853                 attrname : i,
27854                 
27855                 width: item.width,
27856                 //allowBlank:true,
27857                 value: '',
27858                 listeners: {
27859                     'change' : function(f, nv, ov) {
27860                         tb.selectedNode.setAttribute(f.attrname, nv);
27861                     }
27862                 }
27863             }));
27864              
27865         }
27866         tb.addFill();
27867         var _this = this;
27868         tb.addButton( {
27869             text: 'Remove Tag',
27870     
27871             listeners : {
27872                 click : function ()
27873                 {
27874                     // remove
27875                     // undo does not work.
27876                      
27877                     var sn = tb.selectedNode;
27878                     
27879                     var pn = sn.parentNode;
27880                     
27881                     var stn =  sn.childNodes[0];
27882                     var en = sn.childNodes[sn.childNodes.length - 1 ];
27883                     while (sn.childNodes.length) {
27884                         var node = sn.childNodes[0];
27885                         sn.removeChild(node);
27886                         //Roo.log(node);
27887                         pn.insertBefore(node, sn);
27888                         
27889                     }
27890                     pn.removeChild(sn);
27891                     var range = editor.createRange();
27892         
27893                     range.setStart(stn,0);
27894                     range.setEnd(en,0); //????
27895                     //range.selectNode(sel);
27896                     
27897                     
27898                     var selection = editor.getSelection();
27899                     selection.removeAllRanges();
27900                     selection.addRange(range);
27901                     
27902                     
27903                     
27904                     //_this.updateToolbar(null, null, pn);
27905                     _this.updateToolbar(null, null, null);
27906                     _this.footDisp.dom.innerHTML = ''; 
27907                 }
27908             }
27909             
27910                     
27911                 
27912             
27913         });
27914         
27915         
27916         tb.el.on('click', function(e){
27917             e.preventDefault(); // what does this do?
27918         });
27919         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27920         tb.el.hide();
27921         tb.name = nm;
27922         // dont need to disable them... as they will get hidden
27923         return tb;
27924          
27925         
27926     },
27927     buildFooter : function()
27928     {
27929         
27930         var fel = this.editor.wrap.createChild();
27931         this.footer = new Roo.Toolbar(fel);
27932         // toolbar has scrolly on left / right?
27933         var footDisp= new Roo.Toolbar.Fill();
27934         var _t = this;
27935         this.footer.add(
27936             {
27937                 text : '&lt;',
27938                 xtype: 'Button',
27939                 handler : function() {
27940                     _t.footDisp.scrollTo('left',0,true)
27941                 }
27942             }
27943         );
27944         this.footer.add( footDisp );
27945         this.footer.add( 
27946             {
27947                 text : '&gt;',
27948                 xtype: 'Button',
27949                 handler : function() {
27950                     // no animation..
27951                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27952                 }
27953             }
27954         );
27955         var fel = Roo.get(footDisp.el);
27956         fel.addClass('x-editor-context');
27957         this.footDispWrap = fel; 
27958         this.footDispWrap.overflow  = 'hidden';
27959         
27960         this.footDisp = fel.createChild();
27961         this.footDispWrap.on('click', this.onContextClick, this)
27962         
27963         
27964     },
27965     onContextClick : function (ev,dom)
27966     {
27967         ev.preventDefault();
27968         var  cn = dom.className;
27969         //Roo.log(cn);
27970         if (!cn.match(/x-ed-loc-/)) {
27971             return;
27972         }
27973         var n = cn.split('-').pop();
27974         var ans = this.footerEls;
27975         var sel = ans[n];
27976         
27977          // pick
27978         var range = this.editor.createRange();
27979         
27980         range.selectNodeContents(sel);
27981         //range.selectNode(sel);
27982         
27983         
27984         var selection = this.editor.getSelection();
27985         selection.removeAllRanges();
27986         selection.addRange(range);
27987         
27988         
27989         
27990         this.updateToolbar(null, null, sel);
27991         
27992         
27993     }
27994     
27995     
27996     
27997     
27998     
27999 });
28000
28001
28002
28003
28004
28005 /*
28006  * Based on:
28007  * Ext JS Library 1.1.1
28008  * Copyright(c) 2006-2007, Ext JS, LLC.
28009  *
28010  * Originally Released Under LGPL - original licence link has changed is not relivant.
28011  *
28012  * Fork - LGPL
28013  * <script type="text/javascript">
28014  */
28015  
28016 /**
28017  * @class Roo.form.BasicForm
28018  * @extends Roo.util.Observable
28019  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28020  * @constructor
28021  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28022  * @param {Object} config Configuration options
28023  */
28024 Roo.form.BasicForm = function(el, config){
28025     this.allItems = [];
28026     this.childForms = [];
28027     Roo.apply(this, config);
28028     /*
28029      * The Roo.form.Field items in this form.
28030      * @type MixedCollection
28031      */
28032      
28033      
28034     this.items = new Roo.util.MixedCollection(false, function(o){
28035         return o.id || (o.id = Roo.id());
28036     });
28037     this.addEvents({
28038         /**
28039          * @event beforeaction
28040          * Fires before any action is performed. Return false to cancel the action.
28041          * @param {Form} this
28042          * @param {Action} action The action to be performed
28043          */
28044         beforeaction: true,
28045         /**
28046          * @event actionfailed
28047          * Fires when an action fails.
28048          * @param {Form} this
28049          * @param {Action} action The action that failed
28050          */
28051         actionfailed : true,
28052         /**
28053          * @event actioncomplete
28054          * Fires when an action is completed.
28055          * @param {Form} this
28056          * @param {Action} action The action that completed
28057          */
28058         actioncomplete : true
28059     });
28060     if(el){
28061         this.initEl(el);
28062     }
28063     Roo.form.BasicForm.superclass.constructor.call(this);
28064 };
28065
28066 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28067     /**
28068      * @cfg {String} method
28069      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28070      */
28071     /**
28072      * @cfg {DataReader} reader
28073      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28074      * This is optional as there is built-in support for processing JSON.
28075      */
28076     /**
28077      * @cfg {DataReader} errorReader
28078      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28079      * This is completely optional as there is built-in support for processing JSON.
28080      */
28081     /**
28082      * @cfg {String} url
28083      * The URL to use for form actions if one isn't supplied in the action options.
28084      */
28085     /**
28086      * @cfg {Boolean} fileUpload
28087      * Set to true if this form is a file upload.
28088      */
28089      
28090     /**
28091      * @cfg {Object} baseParams
28092      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28093      */
28094      /**
28095      
28096     /**
28097      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28098      */
28099     timeout: 30,
28100
28101     // private
28102     activeAction : null,
28103
28104     /**
28105      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28106      * or setValues() data instead of when the form was first created.
28107      */
28108     trackResetOnLoad : false,
28109     
28110     
28111     /**
28112      * childForms - used for multi-tab forms
28113      * @type {Array}
28114      */
28115     childForms : false,
28116     
28117     /**
28118      * allItems - full list of fields.
28119      * @type {Array}
28120      */
28121     allItems : false,
28122     
28123     /**
28124      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28125      * element by passing it or its id or mask the form itself by passing in true.
28126      * @type Mixed
28127      */
28128     waitMsgTarget : false,
28129
28130     // private
28131     initEl : function(el){
28132         this.el = Roo.get(el);
28133         this.id = this.el.id || Roo.id();
28134         this.el.on('submit', this.onSubmit, this);
28135         this.el.addClass('x-form');
28136     },
28137
28138     // private
28139     onSubmit : function(e){
28140         e.stopEvent();
28141     },
28142
28143     /**
28144      * Returns true if client-side validation on the form is successful.
28145      * @return Boolean
28146      */
28147     isValid : function(){
28148         var valid = true;
28149         this.items.each(function(f){
28150            if(!f.validate()){
28151                valid = false;
28152            }
28153         });
28154         return valid;
28155     },
28156
28157     /**
28158      * Returns true if any fields in this form have changed since their original load.
28159      * @return Boolean
28160      */
28161     isDirty : function(){
28162         var dirty = false;
28163         this.items.each(function(f){
28164            if(f.isDirty()){
28165                dirty = true;
28166                return false;
28167            }
28168         });
28169         return dirty;
28170     },
28171
28172     /**
28173      * Performs a predefined action (submit or load) or custom actions you define on this form.
28174      * @param {String} actionName The name of the action type
28175      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28176      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28177      * accept other config options):
28178      * <pre>
28179 Property          Type             Description
28180 ----------------  ---------------  ----------------------------------------------------------------------------------
28181 url               String           The url for the action (defaults to the form's url)
28182 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28183 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28184 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28185                                    validate the form on the client (defaults to false)
28186      * </pre>
28187      * @return {BasicForm} this
28188      */
28189     doAction : function(action, options){
28190         if(typeof action == 'string'){
28191             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28192         }
28193         if(this.fireEvent('beforeaction', this, action) !== false){
28194             this.beforeAction(action);
28195             action.run.defer(100, action);
28196         }
28197         return this;
28198     },
28199
28200     /**
28201      * Shortcut to do a submit action.
28202      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28203      * @return {BasicForm} this
28204      */
28205     submit : function(options){
28206         this.doAction('submit', options);
28207         return this;
28208     },
28209
28210     /**
28211      * Shortcut to do a load action.
28212      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28213      * @return {BasicForm} this
28214      */
28215     load : function(options){
28216         this.doAction('load', options);
28217         return this;
28218     },
28219
28220     /**
28221      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28222      * @param {Record} record The record to edit
28223      * @return {BasicForm} this
28224      */
28225     updateRecord : function(record){
28226         record.beginEdit();
28227         var fs = record.fields;
28228         fs.each(function(f){
28229             var field = this.findField(f.name);
28230             if(field){
28231                 record.set(f.name, field.getValue());
28232             }
28233         }, this);
28234         record.endEdit();
28235         return this;
28236     },
28237
28238     /**
28239      * Loads an Roo.data.Record into this form.
28240      * @param {Record} record The record to load
28241      * @return {BasicForm} this
28242      */
28243     loadRecord : function(record){
28244         this.setValues(record.data);
28245         return this;
28246     },
28247
28248     // private
28249     beforeAction : function(action){
28250         var o = action.options;
28251         
28252        
28253         if(this.waitMsgTarget === true){
28254             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28255         }else if(this.waitMsgTarget){
28256             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28257             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28258         }else {
28259             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28260         }
28261          
28262     },
28263
28264     // private
28265     afterAction : function(action, success){
28266         this.activeAction = null;
28267         var o = action.options;
28268         
28269         if(this.waitMsgTarget === true){
28270             this.el.unmask();
28271         }else if(this.waitMsgTarget){
28272             this.waitMsgTarget.unmask();
28273         }else{
28274             Roo.MessageBox.updateProgress(1);
28275             Roo.MessageBox.hide();
28276         }
28277          
28278         if(success){
28279             if(o.reset){
28280                 this.reset();
28281             }
28282             Roo.callback(o.success, o.scope, [this, action]);
28283             this.fireEvent('actioncomplete', this, action);
28284             
28285         }else{
28286             
28287             // failure condition..
28288             // we have a scenario where updates need confirming.
28289             // eg. if a locking scenario exists..
28290             // we look for { errors : { needs_confirm : true }} in the response.
28291             if (
28292                 (typeof(action.result) != 'undefined')  &&
28293                 (typeof(action.result.errors) != 'undefined')  &&
28294                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28295            ){
28296                 var _t = this;
28297                 Roo.MessageBox.confirm(
28298                     "Change requires confirmation",
28299                     action.result.errorMsg,
28300                     function(r) {
28301                         if (r != 'yes') {
28302                             return;
28303                         }
28304                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28305                     }
28306                     
28307                 );
28308                 
28309                 
28310                 
28311                 return;
28312             }
28313             
28314             Roo.callback(o.failure, o.scope, [this, action]);
28315             // show an error message if no failed handler is set..
28316             if (!this.hasListener('actionfailed')) {
28317                 Roo.MessageBox.alert("Error",
28318                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28319                         action.result.errorMsg :
28320                         "Saving Failed, please check your entries or try again"
28321                 );
28322             }
28323             
28324             this.fireEvent('actionfailed', this, action);
28325         }
28326         
28327     },
28328
28329     /**
28330      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28331      * @param {String} id The value to search for
28332      * @return Field
28333      */
28334     findField : function(id){
28335         var field = this.items.get(id);
28336         if(!field){
28337             this.items.each(function(f){
28338                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28339                     field = f;
28340                     return false;
28341                 }
28342             });
28343         }
28344         return field || null;
28345     },
28346
28347     /**
28348      * Add a secondary form to this one, 
28349      * Used to provide tabbed forms. One form is primary, with hidden values 
28350      * which mirror the elements from the other forms.
28351      * 
28352      * @param {Roo.form.Form} form to add.
28353      * 
28354      */
28355     addForm : function(form)
28356     {
28357        
28358         if (this.childForms.indexOf(form) > -1) {
28359             // already added..
28360             return;
28361         }
28362         this.childForms.push(form);
28363         var n = '';
28364         Roo.each(form.allItems, function (fe) {
28365             
28366             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28367             if (this.findField(n)) { // already added..
28368                 return;
28369             }
28370             var add = new Roo.form.Hidden({
28371                 name : n
28372             });
28373             add.render(this.el);
28374             
28375             this.add( add );
28376         }, this);
28377         
28378     },
28379     /**
28380      * Mark fields in this form invalid in bulk.
28381      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28382      * @return {BasicForm} this
28383      */
28384     markInvalid : function(errors){
28385         if(errors instanceof Array){
28386             for(var i = 0, len = errors.length; i < len; i++){
28387                 var fieldError = errors[i];
28388                 var f = this.findField(fieldError.id);
28389                 if(f){
28390                     f.markInvalid(fieldError.msg);
28391                 }
28392             }
28393         }else{
28394             var field, id;
28395             for(id in errors){
28396                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28397                     field.markInvalid(errors[id]);
28398                 }
28399             }
28400         }
28401         Roo.each(this.childForms || [], function (f) {
28402             f.markInvalid(errors);
28403         });
28404         
28405         return this;
28406     },
28407
28408     /**
28409      * Set values for fields in this form in bulk.
28410      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28411      * @return {BasicForm} this
28412      */
28413     setValues : function(values){
28414         if(values instanceof Array){ // array of objects
28415             for(var i = 0, len = values.length; i < len; i++){
28416                 var v = values[i];
28417                 var f = this.findField(v.id);
28418                 if(f){
28419                     f.setValue(v.value);
28420                     if(this.trackResetOnLoad){
28421                         f.originalValue = f.getValue();
28422                     }
28423                 }
28424             }
28425         }else{ // object hash
28426             var field, id;
28427             for(id in values){
28428                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28429                     
28430                     if (field.setFromData && 
28431                         field.valueField && 
28432                         field.displayField &&
28433                         // combos' with local stores can 
28434                         // be queried via setValue()
28435                         // to set their value..
28436                         (field.store && !field.store.isLocal)
28437                         ) {
28438                         // it's a combo
28439                         var sd = { };
28440                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28441                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28442                         field.setFromData(sd);
28443                         
28444                     } else {
28445                         field.setValue(values[id]);
28446                     }
28447                     
28448                     
28449                     if(this.trackResetOnLoad){
28450                         field.originalValue = field.getValue();
28451                     }
28452                 }
28453             }
28454         }
28455          
28456         Roo.each(this.childForms || [], function (f) {
28457             f.setValues(values);
28458         });
28459                 
28460         return this;
28461     },
28462
28463     /**
28464      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28465      * they are returned as an array.
28466      * @param {Boolean} asString
28467      * @return {Object}
28468      */
28469     getValues : function(asString){
28470         if (this.childForms) {
28471             // copy values from the child forms
28472             Roo.each(this.childForms, function (f) {
28473                 this.setValues(f.getValues());
28474             }, this);
28475         }
28476         
28477         
28478         
28479         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28480         if(asString === true){
28481             return fs;
28482         }
28483         return Roo.urlDecode(fs);
28484     },
28485     
28486     /**
28487      * Returns the fields in this form as an object with key/value pairs. 
28488      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28489      * @return {Object}
28490      */
28491     getFieldValues : function(with_hidden)
28492     {
28493         if (this.childForms) {
28494             // copy values from the child forms
28495             // should this call getFieldValues - probably not as we do not currently copy
28496             // hidden fields when we generate..
28497             Roo.each(this.childForms, function (f) {
28498                 this.setValues(f.getValues());
28499             }, this);
28500         }
28501         
28502         var ret = {};
28503         this.items.each(function(f){
28504             if (!f.getName()) {
28505                 return;
28506             }
28507             var v = f.getValue();
28508             if (f.inputType =='radio') {
28509                 if (typeof(ret[f.getName()]) == 'undefined') {
28510                     ret[f.getName()] = ''; // empty..
28511                 }
28512                 
28513                 if (!f.el.dom.checked) {
28514                     return;
28515                     
28516                 }
28517                 v = f.el.dom.value;
28518                 
28519             }
28520             
28521             // not sure if this supported any more..
28522             if ((typeof(v) == 'object') && f.getRawValue) {
28523                 v = f.getRawValue() ; // dates..
28524             }
28525             // combo boxes where name != hiddenName...
28526             if (f.name != f.getName()) {
28527                 ret[f.name] = f.getRawValue();
28528             }
28529             ret[f.getName()] = v;
28530         });
28531         
28532         return ret;
28533     },
28534
28535     /**
28536      * Clears all invalid messages in this form.
28537      * @return {BasicForm} this
28538      */
28539     clearInvalid : function(){
28540         this.items.each(function(f){
28541            f.clearInvalid();
28542         });
28543         
28544         Roo.each(this.childForms || [], function (f) {
28545             f.clearInvalid();
28546         });
28547         
28548         
28549         return this;
28550     },
28551
28552     /**
28553      * Resets this form.
28554      * @return {BasicForm} this
28555      */
28556     reset : function(){
28557         this.items.each(function(f){
28558             f.reset();
28559         });
28560         
28561         Roo.each(this.childForms || [], function (f) {
28562             f.reset();
28563         });
28564        
28565         
28566         return this;
28567     },
28568
28569     /**
28570      * Add Roo.form components to this form.
28571      * @param {Field} field1
28572      * @param {Field} field2 (optional)
28573      * @param {Field} etc (optional)
28574      * @return {BasicForm} this
28575      */
28576     add : function(){
28577         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28578         return this;
28579     },
28580
28581
28582     /**
28583      * Removes a field from the items collection (does NOT remove its markup).
28584      * @param {Field} field
28585      * @return {BasicForm} this
28586      */
28587     remove : function(field){
28588         this.items.remove(field);
28589         return this;
28590     },
28591
28592     /**
28593      * Looks at the fields in this form, checks them for an id attribute,
28594      * and calls applyTo on the existing dom element with that id.
28595      * @return {BasicForm} this
28596      */
28597     render : function(){
28598         this.items.each(function(f){
28599             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28600                 f.applyTo(f.id);
28601             }
28602         });
28603         return this;
28604     },
28605
28606     /**
28607      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28608      * @param {Object} values
28609      * @return {BasicForm} this
28610      */
28611     applyToFields : function(o){
28612         this.items.each(function(f){
28613            Roo.apply(f, o);
28614         });
28615         return this;
28616     },
28617
28618     /**
28619      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28620      * @param {Object} values
28621      * @return {BasicForm} this
28622      */
28623     applyIfToFields : function(o){
28624         this.items.each(function(f){
28625            Roo.applyIf(f, o);
28626         });
28627         return this;
28628     }
28629 });
28630
28631 // back compat
28632 Roo.BasicForm = Roo.form.BasicForm;/*
28633  * Based on:
28634  * Ext JS Library 1.1.1
28635  * Copyright(c) 2006-2007, Ext JS, LLC.
28636  *
28637  * Originally Released Under LGPL - original licence link has changed is not relivant.
28638  *
28639  * Fork - LGPL
28640  * <script type="text/javascript">
28641  */
28642
28643 /**
28644  * @class Roo.form.Form
28645  * @extends Roo.form.BasicForm
28646  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28647  * @constructor
28648  * @param {Object} config Configuration options
28649  */
28650 Roo.form.Form = function(config){
28651     var xitems =  [];
28652     if (config.items) {
28653         xitems = config.items;
28654         delete config.items;
28655     }
28656    
28657     
28658     Roo.form.Form.superclass.constructor.call(this, null, config);
28659     this.url = this.url || this.action;
28660     if(!this.root){
28661         this.root = new Roo.form.Layout(Roo.applyIf({
28662             id: Roo.id()
28663         }, config));
28664     }
28665     this.active = this.root;
28666     /**
28667      * Array of all the buttons that have been added to this form via {@link addButton}
28668      * @type Array
28669      */
28670     this.buttons = [];
28671     this.allItems = [];
28672     this.addEvents({
28673         /**
28674          * @event clientvalidation
28675          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28676          * @param {Form} this
28677          * @param {Boolean} valid true if the form has passed client-side validation
28678          */
28679         clientvalidation: true,
28680         /**
28681          * @event rendered
28682          * Fires when the form is rendered
28683          * @param {Roo.form.Form} form
28684          */
28685         rendered : true
28686     });
28687     
28688     if (this.progressUrl) {
28689             // push a hidden field onto the list of fields..
28690             this.addxtype( {
28691                     xns: Roo.form, 
28692                     xtype : 'Hidden', 
28693                     name : 'UPLOAD_IDENTIFIER' 
28694             });
28695         }
28696         
28697     
28698     Roo.each(xitems, this.addxtype, this);
28699     
28700     
28701     
28702 };
28703
28704 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28705     /**
28706      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28707      */
28708     /**
28709      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28710      */
28711     /**
28712      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28713      */
28714     buttonAlign:'center',
28715
28716     /**
28717      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28718      */
28719     minButtonWidth:75,
28720
28721     /**
28722      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28723      * This property cascades to child containers if not set.
28724      */
28725     labelAlign:'left',
28726
28727     /**
28728      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28729      * fires a looping event with that state. This is required to bind buttons to the valid
28730      * state using the config value formBind:true on the button.
28731      */
28732     monitorValid : false,
28733
28734     /**
28735      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28736      */
28737     monitorPoll : 200,
28738     
28739     /**
28740      * @cfg {String} progressUrl - Url to return progress data 
28741      */
28742     
28743     progressUrl : false,
28744   
28745     /**
28746      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28747      * fields are added and the column is closed. If no fields are passed the column remains open
28748      * until end() is called.
28749      * @param {Object} config The config to pass to the column
28750      * @param {Field} field1 (optional)
28751      * @param {Field} field2 (optional)
28752      * @param {Field} etc (optional)
28753      * @return Column The column container object
28754      */
28755     column : function(c){
28756         var col = new Roo.form.Column(c);
28757         this.start(col);
28758         if(arguments.length > 1){ // duplicate code required because of Opera
28759             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28760             this.end();
28761         }
28762         return col;
28763     },
28764
28765     /**
28766      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28767      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28768      * until end() is called.
28769      * @param {Object} config The config to pass to the fieldset
28770      * @param {Field} field1 (optional)
28771      * @param {Field} field2 (optional)
28772      * @param {Field} etc (optional)
28773      * @return FieldSet The fieldset container object
28774      */
28775     fieldset : function(c){
28776         var fs = new Roo.form.FieldSet(c);
28777         this.start(fs);
28778         if(arguments.length > 1){ // duplicate code required because of Opera
28779             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28780             this.end();
28781         }
28782         return fs;
28783     },
28784
28785     /**
28786      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28787      * fields are added and the container is closed. If no fields are passed the container remains open
28788      * until end() is called.
28789      * @param {Object} config The config to pass to the Layout
28790      * @param {Field} field1 (optional)
28791      * @param {Field} field2 (optional)
28792      * @param {Field} etc (optional)
28793      * @return Layout The container object
28794      */
28795     container : function(c){
28796         var l = new Roo.form.Layout(c);
28797         this.start(l);
28798         if(arguments.length > 1){ // duplicate code required because of Opera
28799             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28800             this.end();
28801         }
28802         return l;
28803     },
28804
28805     /**
28806      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28807      * @param {Object} container A Roo.form.Layout or subclass of Layout
28808      * @return {Form} this
28809      */
28810     start : function(c){
28811         // cascade label info
28812         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28813         this.active.stack.push(c);
28814         c.ownerCt = this.active;
28815         this.active = c;
28816         return this;
28817     },
28818
28819     /**
28820      * Closes the current open container
28821      * @return {Form} this
28822      */
28823     end : function(){
28824         if(this.active == this.root){
28825             return this;
28826         }
28827         this.active = this.active.ownerCt;
28828         return this;
28829     },
28830
28831     /**
28832      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28833      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28834      * as the label of the field.
28835      * @param {Field} field1
28836      * @param {Field} field2 (optional)
28837      * @param {Field} etc. (optional)
28838      * @return {Form} this
28839      */
28840     add : function(){
28841         this.active.stack.push.apply(this.active.stack, arguments);
28842         this.allItems.push.apply(this.allItems,arguments);
28843         var r = [];
28844         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28845             if(a[i].isFormField){
28846                 r.push(a[i]);
28847             }
28848         }
28849         if(r.length > 0){
28850             Roo.form.Form.superclass.add.apply(this, r);
28851         }
28852         return this;
28853     },
28854     
28855
28856     
28857     
28858     
28859      /**
28860      * Find any element that has been added to a form, using it's ID or name
28861      * This can include framesets, columns etc. along with regular fields..
28862      * @param {String} id - id or name to find.
28863      
28864      * @return {Element} e - or false if nothing found.
28865      */
28866     findbyId : function(id)
28867     {
28868         var ret = false;
28869         if (!id) {
28870             return ret;
28871         }
28872         Roo.each(this.allItems, function(f){
28873             if (f.id == id || f.name == id ){
28874                 ret = f;
28875                 return false;
28876             }
28877         });
28878         return ret;
28879     },
28880
28881     
28882     
28883     /**
28884      * Render this form into the passed container. This should only be called once!
28885      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28886      * @return {Form} this
28887      */
28888     render : function(ct)
28889     {
28890         
28891         
28892         
28893         ct = Roo.get(ct);
28894         var o = this.autoCreate || {
28895             tag: 'form',
28896             method : this.method || 'POST',
28897             id : this.id || Roo.id()
28898         };
28899         this.initEl(ct.createChild(o));
28900
28901         this.root.render(this.el);
28902         
28903        
28904              
28905         this.items.each(function(f){
28906             f.render('x-form-el-'+f.id);
28907         });
28908
28909         if(this.buttons.length > 0){
28910             // tables are required to maintain order and for correct IE layout
28911             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28912                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28913                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28914             }}, null, true);
28915             var tr = tb.getElementsByTagName('tr')[0];
28916             for(var i = 0, len = this.buttons.length; i < len; i++) {
28917                 var b = this.buttons[i];
28918                 var td = document.createElement('td');
28919                 td.className = 'x-form-btn-td';
28920                 b.render(tr.appendChild(td));
28921             }
28922         }
28923         if(this.monitorValid){ // initialize after render
28924             this.startMonitoring();
28925         }
28926         this.fireEvent('rendered', this);
28927         return this;
28928     },
28929
28930     /**
28931      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28932      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28933      * object or a valid Roo.DomHelper element config
28934      * @param {Function} handler The function called when the button is clicked
28935      * @param {Object} scope (optional) The scope of the handler function
28936      * @return {Roo.Button}
28937      */
28938     addButton : function(config, handler, scope){
28939         var bc = {
28940             handler: handler,
28941             scope: scope,
28942             minWidth: this.minButtonWidth,
28943             hideParent:true
28944         };
28945         if(typeof config == "string"){
28946             bc.text = config;
28947         }else{
28948             Roo.apply(bc, config);
28949         }
28950         var btn = new Roo.Button(null, bc);
28951         this.buttons.push(btn);
28952         return btn;
28953     },
28954
28955      /**
28956      * Adds a series of form elements (using the xtype property as the factory method.
28957      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28958      * @param {Object} config 
28959      */
28960     
28961     addxtype : function()
28962     {
28963         var ar = Array.prototype.slice.call(arguments, 0);
28964         var ret = false;
28965         for(var i = 0; i < ar.length; i++) {
28966             if (!ar[i]) {
28967                 continue; // skip -- if this happends something invalid got sent, we 
28968                 // should ignore it, as basically that interface element will not show up
28969                 // and that should be pretty obvious!!
28970             }
28971             
28972             if (Roo.form[ar[i].xtype]) {
28973                 ar[i].form = this;
28974                 var fe = Roo.factory(ar[i], Roo.form);
28975                 if (!ret) {
28976                     ret = fe;
28977                 }
28978                 fe.form = this;
28979                 if (fe.store) {
28980                     fe.store.form = this;
28981                 }
28982                 if (fe.isLayout) {  
28983                          
28984                     this.start(fe);
28985                     this.allItems.push(fe);
28986                     if (fe.items && fe.addxtype) {
28987                         fe.addxtype.apply(fe, fe.items);
28988                         delete fe.items;
28989                     }
28990                      this.end();
28991                     continue;
28992                 }
28993                 
28994                 
28995                  
28996                 this.add(fe);
28997               //  console.log('adding ' + ar[i].xtype);
28998             }
28999             if (ar[i].xtype == 'Button') {  
29000                 //console.log('adding button');
29001                 //console.log(ar[i]);
29002                 this.addButton(ar[i]);
29003                 this.allItems.push(fe);
29004                 continue;
29005             }
29006             
29007             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29008                 alert('end is not supported on xtype any more, use items');
29009             //    this.end();
29010             //    //console.log('adding end');
29011             }
29012             
29013         }
29014         return ret;
29015     },
29016     
29017     /**
29018      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29019      * option "monitorValid"
29020      */
29021     startMonitoring : function(){
29022         if(!this.bound){
29023             this.bound = true;
29024             Roo.TaskMgr.start({
29025                 run : this.bindHandler,
29026                 interval : this.monitorPoll || 200,
29027                 scope: this
29028             });
29029         }
29030     },
29031
29032     /**
29033      * Stops monitoring of the valid state of this form
29034      */
29035     stopMonitoring : function(){
29036         this.bound = false;
29037     },
29038
29039     // private
29040     bindHandler : function(){
29041         if(!this.bound){
29042             return false; // stops binding
29043         }
29044         var valid = true;
29045         this.items.each(function(f){
29046             if(!f.isValid(true)){
29047                 valid = false;
29048                 return false;
29049             }
29050         });
29051         for(var i = 0, len = this.buttons.length; i < len; i++){
29052             var btn = this.buttons[i];
29053             if(btn.formBind === true && btn.disabled === valid){
29054                 btn.setDisabled(!valid);
29055             }
29056         }
29057         this.fireEvent('clientvalidation', this, valid);
29058     }
29059     
29060     
29061     
29062     
29063     
29064     
29065     
29066     
29067 });
29068
29069
29070 // back compat
29071 Roo.Form = Roo.form.Form;
29072 /*
29073  * Based on:
29074  * Ext JS Library 1.1.1
29075  * Copyright(c) 2006-2007, Ext JS, LLC.
29076  *
29077  * Originally Released Under LGPL - original licence link has changed is not relivant.
29078  *
29079  * Fork - LGPL
29080  * <script type="text/javascript">
29081  */
29082  
29083  /**
29084  * @class Roo.form.Action
29085  * Internal Class used to handle form actions
29086  * @constructor
29087  * @param {Roo.form.BasicForm} el The form element or its id
29088  * @param {Object} config Configuration options
29089  */
29090  
29091  
29092 // define the action interface
29093 Roo.form.Action = function(form, options){
29094     this.form = form;
29095     this.options = options || {};
29096 };
29097 /**
29098  * Client Validation Failed
29099  * @const 
29100  */
29101 Roo.form.Action.CLIENT_INVALID = 'client';
29102 /**
29103  * Server Validation Failed
29104  * @const 
29105  */
29106  Roo.form.Action.SERVER_INVALID = 'server';
29107  /**
29108  * Connect to Server Failed
29109  * @const 
29110  */
29111 Roo.form.Action.CONNECT_FAILURE = 'connect';
29112 /**
29113  * Reading Data from Server Failed
29114  * @const 
29115  */
29116 Roo.form.Action.LOAD_FAILURE = 'load';
29117
29118 Roo.form.Action.prototype = {
29119     type : 'default',
29120     failureType : undefined,
29121     response : undefined,
29122     result : undefined,
29123
29124     // interface method
29125     run : function(options){
29126
29127     },
29128
29129     // interface method
29130     success : function(response){
29131
29132     },
29133
29134     // interface method
29135     handleResponse : function(response){
29136
29137     },
29138
29139     // default connection failure
29140     failure : function(response){
29141         
29142         this.response = response;
29143         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29144         this.form.afterAction(this, false);
29145     },
29146
29147     processResponse : function(response){
29148         this.response = response;
29149         if(!response.responseText){
29150             return true;
29151         }
29152         this.result = this.handleResponse(response);
29153         return this.result;
29154     },
29155
29156     // utility functions used internally
29157     getUrl : function(appendParams){
29158         var url = this.options.url || this.form.url || this.form.el.dom.action;
29159         if(appendParams){
29160             var p = this.getParams();
29161             if(p){
29162                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29163             }
29164         }
29165         return url;
29166     },
29167
29168     getMethod : function(){
29169         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29170     },
29171
29172     getParams : function(){
29173         var bp = this.form.baseParams;
29174         var p = this.options.params;
29175         if(p){
29176             if(typeof p == "object"){
29177                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29178             }else if(typeof p == 'string' && bp){
29179                 p += '&' + Roo.urlEncode(bp);
29180             }
29181         }else if(bp){
29182             p = Roo.urlEncode(bp);
29183         }
29184         return p;
29185     },
29186
29187     createCallback : function(){
29188         return {
29189             success: this.success,
29190             failure: this.failure,
29191             scope: this,
29192             timeout: (this.form.timeout*1000),
29193             upload: this.form.fileUpload ? this.success : undefined
29194         };
29195     }
29196 };
29197
29198 Roo.form.Action.Submit = function(form, options){
29199     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29200 };
29201
29202 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29203     type : 'submit',
29204
29205     haveProgress : false,
29206     uploadComplete : false,
29207     
29208     // uploadProgress indicator.
29209     uploadProgress : function()
29210     {
29211         if (!this.form.progressUrl) {
29212             return;
29213         }
29214         
29215         if (!this.haveProgress) {
29216             Roo.MessageBox.progress("Uploading", "Uploading");
29217         }
29218         if (this.uploadComplete) {
29219            Roo.MessageBox.hide();
29220            return;
29221         }
29222         
29223         this.haveProgress = true;
29224    
29225         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29226         
29227         var c = new Roo.data.Connection();
29228         c.request({
29229             url : this.form.progressUrl,
29230             params: {
29231                 id : uid
29232             },
29233             method: 'GET',
29234             success : function(req){
29235                //console.log(data);
29236                 var rdata = false;
29237                 var edata;
29238                 try  {
29239                    rdata = Roo.decode(req.responseText)
29240                 } catch (e) {
29241                     Roo.log("Invalid data from server..");
29242                     Roo.log(edata);
29243                     return;
29244                 }
29245                 if (!rdata || !rdata.success) {
29246                     Roo.log(rdata);
29247                     Roo.MessageBox.alert(Roo.encode(rdata));
29248                     return;
29249                 }
29250                 var data = rdata.data;
29251                 
29252                 if (this.uploadComplete) {
29253                    Roo.MessageBox.hide();
29254                    return;
29255                 }
29256                    
29257                 if (data){
29258                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29259                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29260                     );
29261                 }
29262                 this.uploadProgress.defer(2000,this);
29263             },
29264        
29265             failure: function(data) {
29266                 Roo.log('progress url failed ');
29267                 Roo.log(data);
29268             },
29269             scope : this
29270         });
29271            
29272     },
29273     
29274     
29275     run : function()
29276     {
29277         // run get Values on the form, so it syncs any secondary forms.
29278         this.form.getValues();
29279         
29280         var o = this.options;
29281         var method = this.getMethod();
29282         var isPost = method == 'POST';
29283         if(o.clientValidation === false || this.form.isValid()){
29284             
29285             if (this.form.progressUrl) {
29286                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29287                     (new Date() * 1) + '' + Math.random());
29288                     
29289             } 
29290             
29291             
29292             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29293                 form:this.form.el.dom,
29294                 url:this.getUrl(!isPost),
29295                 method: method,
29296                 params:isPost ? this.getParams() : null,
29297                 isUpload: this.form.fileUpload
29298             }));
29299             
29300             this.uploadProgress();
29301
29302         }else if (o.clientValidation !== false){ // client validation failed
29303             this.failureType = Roo.form.Action.CLIENT_INVALID;
29304             this.form.afterAction(this, false);
29305         }
29306     },
29307
29308     success : function(response)
29309     {
29310         this.uploadComplete= true;
29311         if (this.haveProgress) {
29312             Roo.MessageBox.hide();
29313         }
29314         
29315         
29316         var result = this.processResponse(response);
29317         if(result === true || result.success){
29318             this.form.afterAction(this, true);
29319             return;
29320         }
29321         if(result.errors){
29322             this.form.markInvalid(result.errors);
29323             this.failureType = Roo.form.Action.SERVER_INVALID;
29324         }
29325         this.form.afterAction(this, false);
29326     },
29327     failure : function(response)
29328     {
29329         this.uploadComplete= true;
29330         if (this.haveProgress) {
29331             Roo.MessageBox.hide();
29332         }
29333         
29334         this.response = response;
29335         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29336         this.form.afterAction(this, false);
29337     },
29338     
29339     handleResponse : function(response){
29340         if(this.form.errorReader){
29341             var rs = this.form.errorReader.read(response);
29342             var errors = [];
29343             if(rs.records){
29344                 for(var i = 0, len = rs.records.length; i < len; i++) {
29345                     var r = rs.records[i];
29346                     errors[i] = r.data;
29347                 }
29348             }
29349             if(errors.length < 1){
29350                 errors = null;
29351             }
29352             return {
29353                 success : rs.success,
29354                 errors : errors
29355             };
29356         }
29357         var ret = false;
29358         try {
29359             ret = Roo.decode(response.responseText);
29360         } catch (e) {
29361             ret = {
29362                 success: false,
29363                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29364                 errors : []
29365             };
29366         }
29367         return ret;
29368         
29369     }
29370 });
29371
29372
29373 Roo.form.Action.Load = function(form, options){
29374     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29375     this.reader = this.form.reader;
29376 };
29377
29378 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29379     type : 'load',
29380
29381     run : function(){
29382         
29383         Roo.Ajax.request(Roo.apply(
29384                 this.createCallback(), {
29385                     method:this.getMethod(),
29386                     url:this.getUrl(false),
29387                     params:this.getParams()
29388         }));
29389     },
29390
29391     success : function(response){
29392         
29393         var result = this.processResponse(response);
29394         if(result === true || !result.success || !result.data){
29395             this.failureType = Roo.form.Action.LOAD_FAILURE;
29396             this.form.afterAction(this, false);
29397             return;
29398         }
29399         this.form.clearInvalid();
29400         this.form.setValues(result.data);
29401         this.form.afterAction(this, true);
29402     },
29403
29404     handleResponse : function(response){
29405         if(this.form.reader){
29406             var rs = this.form.reader.read(response);
29407             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29408             return {
29409                 success : rs.success,
29410                 data : data
29411             };
29412         }
29413         return Roo.decode(response.responseText);
29414     }
29415 });
29416
29417 Roo.form.Action.ACTION_TYPES = {
29418     'load' : Roo.form.Action.Load,
29419     'submit' : Roo.form.Action.Submit
29420 };/*
29421  * Based on:
29422  * Ext JS Library 1.1.1
29423  * Copyright(c) 2006-2007, Ext JS, LLC.
29424  *
29425  * Originally Released Under LGPL - original licence link has changed is not relivant.
29426  *
29427  * Fork - LGPL
29428  * <script type="text/javascript">
29429  */
29430  
29431 /**
29432  * @class Roo.form.Layout
29433  * @extends Roo.Component
29434  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29435  * @constructor
29436  * @param {Object} config Configuration options
29437  */
29438 Roo.form.Layout = function(config){
29439     var xitems = [];
29440     if (config.items) {
29441         xitems = config.items;
29442         delete config.items;
29443     }
29444     Roo.form.Layout.superclass.constructor.call(this, config);
29445     this.stack = [];
29446     Roo.each(xitems, this.addxtype, this);
29447      
29448 };
29449
29450 Roo.extend(Roo.form.Layout, Roo.Component, {
29451     /**
29452      * @cfg {String/Object} autoCreate
29453      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29454      */
29455     /**
29456      * @cfg {String/Object/Function} style
29457      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29458      * a function which returns such a specification.
29459      */
29460     /**
29461      * @cfg {String} labelAlign
29462      * Valid values are "left," "top" and "right" (defaults to "left")
29463      */
29464     /**
29465      * @cfg {Number} labelWidth
29466      * Fixed width in pixels of all field labels (defaults to undefined)
29467      */
29468     /**
29469      * @cfg {Boolean} clear
29470      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29471      */
29472     clear : true,
29473     /**
29474      * @cfg {String} labelSeparator
29475      * The separator to use after field labels (defaults to ':')
29476      */
29477     labelSeparator : ':',
29478     /**
29479      * @cfg {Boolean} hideLabels
29480      * True to suppress the display of field labels in this layout (defaults to false)
29481      */
29482     hideLabels : false,
29483
29484     // private
29485     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29486     
29487     isLayout : true,
29488     
29489     // private
29490     onRender : function(ct, position){
29491         if(this.el){ // from markup
29492             this.el = Roo.get(this.el);
29493         }else {  // generate
29494             var cfg = this.getAutoCreate();
29495             this.el = ct.createChild(cfg, position);
29496         }
29497         if(this.style){
29498             this.el.applyStyles(this.style);
29499         }
29500         if(this.labelAlign){
29501             this.el.addClass('x-form-label-'+this.labelAlign);
29502         }
29503         if(this.hideLabels){
29504             this.labelStyle = "display:none";
29505             this.elementStyle = "padding-left:0;";
29506         }else{
29507             if(typeof this.labelWidth == 'number'){
29508                 this.labelStyle = "width:"+this.labelWidth+"px;";
29509                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29510             }
29511             if(this.labelAlign == 'top'){
29512                 this.labelStyle = "width:auto;";
29513                 this.elementStyle = "padding-left:0;";
29514             }
29515         }
29516         var stack = this.stack;
29517         var slen = stack.length;
29518         if(slen > 0){
29519             if(!this.fieldTpl){
29520                 var t = new Roo.Template(
29521                     '<div class="x-form-item {5}">',
29522                         '<label for="{0}" style="{2}">{1}{4}</label>',
29523                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29524                         '</div>',
29525                     '</div><div class="x-form-clear-left"></div>'
29526                 );
29527                 t.disableFormats = true;
29528                 t.compile();
29529                 Roo.form.Layout.prototype.fieldTpl = t;
29530             }
29531             for(var i = 0; i < slen; i++) {
29532                 if(stack[i].isFormField){
29533                     this.renderField(stack[i]);
29534                 }else{
29535                     this.renderComponent(stack[i]);
29536                 }
29537             }
29538         }
29539         if(this.clear){
29540             this.el.createChild({cls:'x-form-clear'});
29541         }
29542     },
29543
29544     // private
29545     renderField : function(f){
29546         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29547                f.id, //0
29548                f.fieldLabel, //1
29549                f.labelStyle||this.labelStyle||'', //2
29550                this.elementStyle||'', //3
29551                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29552                f.itemCls||this.itemCls||''  //5
29553        ], true).getPrevSibling());
29554     },
29555
29556     // private
29557     renderComponent : function(c){
29558         c.render(c.isLayout ? this.el : this.el.createChild());    
29559     },
29560     /**
29561      * Adds a object form elements (using the xtype property as the factory method.)
29562      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29563      * @param {Object} config 
29564      */
29565     addxtype : function(o)
29566     {
29567         // create the lement.
29568         o.form = this.form;
29569         var fe = Roo.factory(o, Roo.form);
29570         this.form.allItems.push(fe);
29571         this.stack.push(fe);
29572         
29573         if (fe.isFormField) {
29574             this.form.items.add(fe);
29575         }
29576          
29577         return fe;
29578     }
29579 });
29580
29581 /**
29582  * @class Roo.form.Column
29583  * @extends Roo.form.Layout
29584  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29585  * @constructor
29586  * @param {Object} config Configuration options
29587  */
29588 Roo.form.Column = function(config){
29589     Roo.form.Column.superclass.constructor.call(this, config);
29590 };
29591
29592 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29593     /**
29594      * @cfg {Number/String} width
29595      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29596      */
29597     /**
29598      * @cfg {String/Object} autoCreate
29599      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29600      */
29601
29602     // private
29603     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29604
29605     // private
29606     onRender : function(ct, position){
29607         Roo.form.Column.superclass.onRender.call(this, ct, position);
29608         if(this.width){
29609             this.el.setWidth(this.width);
29610         }
29611     }
29612 });
29613
29614
29615 /**
29616  * @class Roo.form.Row
29617  * @extends Roo.form.Layout
29618  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29619  * @constructor
29620  * @param {Object} config Configuration options
29621  */
29622
29623  
29624 Roo.form.Row = function(config){
29625     Roo.form.Row.superclass.constructor.call(this, config);
29626 };
29627  
29628 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29629       /**
29630      * @cfg {Number/String} width
29631      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29632      */
29633     /**
29634      * @cfg {Number/String} height
29635      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29636      */
29637     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29638     
29639     padWidth : 20,
29640     // private
29641     onRender : function(ct, position){
29642         //console.log('row render');
29643         if(!this.rowTpl){
29644             var t = new Roo.Template(
29645                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29646                     '<label for="{0}" style="{2}">{1}{4}</label>',
29647                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29648                     '</div>',
29649                 '</div>'
29650             );
29651             t.disableFormats = true;
29652             t.compile();
29653             Roo.form.Layout.prototype.rowTpl = t;
29654         }
29655         this.fieldTpl = this.rowTpl;
29656         
29657         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29658         var labelWidth = 100;
29659         
29660         if ((this.labelAlign != 'top')) {
29661             if (typeof this.labelWidth == 'number') {
29662                 labelWidth = this.labelWidth
29663             }
29664             this.padWidth =  20 + labelWidth;
29665             
29666         }
29667         
29668         Roo.form.Column.superclass.onRender.call(this, ct, position);
29669         if(this.width){
29670             this.el.setWidth(this.width);
29671         }
29672         if(this.height){
29673             this.el.setHeight(this.height);
29674         }
29675     },
29676     
29677     // private
29678     renderField : function(f){
29679         f.fieldEl = this.fieldTpl.append(this.el, [
29680                f.id, f.fieldLabel,
29681                f.labelStyle||this.labelStyle||'',
29682                this.elementStyle||'',
29683                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29684                f.itemCls||this.itemCls||'',
29685                f.width ? f.width + this.padWidth : 160 + this.padWidth
29686        ],true);
29687     }
29688 });
29689  
29690
29691 /**
29692  * @class Roo.form.FieldSet
29693  * @extends Roo.form.Layout
29694  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29695  * @constructor
29696  * @param {Object} config Configuration options
29697  */
29698 Roo.form.FieldSet = function(config){
29699     Roo.form.FieldSet.superclass.constructor.call(this, config);
29700 };
29701
29702 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29703     /**
29704      * @cfg {String} legend
29705      * The text to display as the legend for the FieldSet (defaults to '')
29706      */
29707     /**
29708      * @cfg {String/Object} autoCreate
29709      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29710      */
29711
29712     // private
29713     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29714
29715     // private
29716     onRender : function(ct, position){
29717         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29718         if(this.legend){
29719             this.setLegend(this.legend);
29720         }
29721     },
29722
29723     // private
29724     setLegend : function(text){
29725         if(this.rendered){
29726             this.el.child('legend').update(text);
29727         }
29728     }
29729 });/*
29730  * Based on:
29731  * Ext JS Library 1.1.1
29732  * Copyright(c) 2006-2007, Ext JS, LLC.
29733  *
29734  * Originally Released Under LGPL - original licence link has changed is not relivant.
29735  *
29736  * Fork - LGPL
29737  * <script type="text/javascript">
29738  */
29739 /**
29740  * @class Roo.form.VTypes
29741  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29742  * @singleton
29743  */
29744 Roo.form.VTypes = function(){
29745     // closure these in so they are only created once.
29746     var alpha = /^[a-zA-Z_]+$/;
29747     var alphanum = /^[a-zA-Z0-9_]+$/;
29748     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29749     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29750
29751     // All these messages and functions are configurable
29752     return {
29753         /**
29754          * The function used to validate email addresses
29755          * @param {String} value The email address
29756          */
29757         'email' : function(v){
29758             return email.test(v);
29759         },
29760         /**
29761          * The error text to display when the email validation function returns false
29762          * @type String
29763          */
29764         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29765         /**
29766          * The keystroke filter mask to be applied on email input
29767          * @type RegExp
29768          */
29769         'emailMask' : /[a-z0-9_\.\-@]/i,
29770
29771         /**
29772          * The function used to validate URLs
29773          * @param {String} value The URL
29774          */
29775         'url' : function(v){
29776             return url.test(v);
29777         },
29778         /**
29779          * The error text to display when the url validation function returns false
29780          * @type String
29781          */
29782         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29783         
29784         /**
29785          * The function used to validate alpha values
29786          * @param {String} value The value
29787          */
29788         'alpha' : function(v){
29789             return alpha.test(v);
29790         },
29791         /**
29792          * The error text to display when the alpha validation function returns false
29793          * @type String
29794          */
29795         'alphaText' : 'This field should only contain letters and _',
29796         /**
29797          * The keystroke filter mask to be applied on alpha input
29798          * @type RegExp
29799          */
29800         'alphaMask' : /[a-z_]/i,
29801
29802         /**
29803          * The function used to validate alphanumeric values
29804          * @param {String} value The value
29805          */
29806         'alphanum' : function(v){
29807             return alphanum.test(v);
29808         },
29809         /**
29810          * The error text to display when the alphanumeric validation function returns false
29811          * @type String
29812          */
29813         'alphanumText' : 'This field should only contain letters, numbers and _',
29814         /**
29815          * The keystroke filter mask to be applied on alphanumeric input
29816          * @type RegExp
29817          */
29818         'alphanumMask' : /[a-z0-9_]/i
29819     };
29820 }();//<script type="text/javascript">
29821
29822 /**
29823  * @class Roo.form.FCKeditor
29824  * @extends Roo.form.TextArea
29825  * Wrapper around the FCKEditor http://www.fckeditor.net
29826  * @constructor
29827  * Creates a new FCKeditor
29828  * @param {Object} config Configuration options
29829  */
29830 Roo.form.FCKeditor = function(config){
29831     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29832     this.addEvents({
29833          /**
29834          * @event editorinit
29835          * Fired when the editor is initialized - you can add extra handlers here..
29836          * @param {FCKeditor} this
29837          * @param {Object} the FCK object.
29838          */
29839         editorinit : true
29840     });
29841     
29842     
29843 };
29844 Roo.form.FCKeditor.editors = { };
29845 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29846 {
29847     //defaultAutoCreate : {
29848     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29849     //},
29850     // private
29851     /**
29852      * @cfg {Object} fck options - see fck manual for details.
29853      */
29854     fckconfig : false,
29855     
29856     /**
29857      * @cfg {Object} fck toolbar set (Basic or Default)
29858      */
29859     toolbarSet : 'Basic',
29860     /**
29861      * @cfg {Object} fck BasePath
29862      */ 
29863     basePath : '/fckeditor/',
29864     
29865     
29866     frame : false,
29867     
29868     value : '',
29869     
29870    
29871     onRender : function(ct, position)
29872     {
29873         if(!this.el){
29874             this.defaultAutoCreate = {
29875                 tag: "textarea",
29876                 style:"width:300px;height:60px;",
29877                 autocomplete: "off"
29878             };
29879         }
29880         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29881         /*
29882         if(this.grow){
29883             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29884             if(this.preventScrollbars){
29885                 this.el.setStyle("overflow", "hidden");
29886             }
29887             this.el.setHeight(this.growMin);
29888         }
29889         */
29890         //console.log('onrender' + this.getId() );
29891         Roo.form.FCKeditor.editors[this.getId()] = this;
29892          
29893
29894         this.replaceTextarea() ;
29895         
29896     },
29897     
29898     getEditor : function() {
29899         return this.fckEditor;
29900     },
29901     /**
29902      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29903      * @param {Mixed} value The value to set
29904      */
29905     
29906     
29907     setValue : function(value)
29908     {
29909         //console.log('setValue: ' + value);
29910         
29911         if(typeof(value) == 'undefined') { // not sure why this is happending...
29912             return;
29913         }
29914         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29915         
29916         //if(!this.el || !this.getEditor()) {
29917         //    this.value = value;
29918             //this.setValue.defer(100,this,[value]);    
29919         //    return;
29920         //} 
29921         
29922         if(!this.getEditor()) {
29923             return;
29924         }
29925         
29926         this.getEditor().SetData(value);
29927         
29928         //
29929
29930     },
29931
29932     /**
29933      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29934      * @return {Mixed} value The field value
29935      */
29936     getValue : function()
29937     {
29938         
29939         if (this.frame && this.frame.dom.style.display == 'none') {
29940             return Roo.form.FCKeditor.superclass.getValue.call(this);
29941         }
29942         
29943         if(!this.el || !this.getEditor()) {
29944            
29945            // this.getValue.defer(100,this); 
29946             return this.value;
29947         }
29948        
29949         
29950         var value=this.getEditor().GetData();
29951         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29952         return Roo.form.FCKeditor.superclass.getValue.call(this);
29953         
29954
29955     },
29956
29957     /**
29958      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29959      * @return {Mixed} value The field value
29960      */
29961     getRawValue : function()
29962     {
29963         if (this.frame && this.frame.dom.style.display == 'none') {
29964             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29965         }
29966         
29967         if(!this.el || !this.getEditor()) {
29968             //this.getRawValue.defer(100,this); 
29969             return this.value;
29970             return;
29971         }
29972         
29973         
29974         
29975         var value=this.getEditor().GetData();
29976         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29977         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29978          
29979     },
29980     
29981     setSize : function(w,h) {
29982         
29983         
29984         
29985         //if (this.frame && this.frame.dom.style.display == 'none') {
29986         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29987         //    return;
29988         //}
29989         //if(!this.el || !this.getEditor()) {
29990         //    this.setSize.defer(100,this, [w,h]); 
29991         //    return;
29992         //}
29993         
29994         
29995         
29996         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29997         
29998         this.frame.dom.setAttribute('width', w);
29999         this.frame.dom.setAttribute('height', h);
30000         this.frame.setSize(w,h);
30001         
30002     },
30003     
30004     toggleSourceEdit : function(value) {
30005         
30006       
30007          
30008         this.el.dom.style.display = value ? '' : 'none';
30009         this.frame.dom.style.display = value ?  'none' : '';
30010         
30011     },
30012     
30013     
30014     focus: function(tag)
30015     {
30016         if (this.frame.dom.style.display == 'none') {
30017             return Roo.form.FCKeditor.superclass.focus.call(this);
30018         }
30019         if(!this.el || !this.getEditor()) {
30020             this.focus.defer(100,this, [tag]); 
30021             return;
30022         }
30023         
30024         
30025         
30026         
30027         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30028         this.getEditor().Focus();
30029         if (tgs.length) {
30030             if (!this.getEditor().Selection.GetSelection()) {
30031                 this.focus.defer(100,this, [tag]); 
30032                 return;
30033             }
30034             
30035             
30036             var r = this.getEditor().EditorDocument.createRange();
30037             r.setStart(tgs[0],0);
30038             r.setEnd(tgs[0],0);
30039             this.getEditor().Selection.GetSelection().removeAllRanges();
30040             this.getEditor().Selection.GetSelection().addRange(r);
30041             this.getEditor().Focus();
30042         }
30043         
30044     },
30045     
30046     
30047     
30048     replaceTextarea : function()
30049     {
30050         if ( document.getElementById( this.getId() + '___Frame' ) )
30051             return ;
30052         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30053         //{
30054             // We must check the elements firstly using the Id and then the name.
30055         var oTextarea = document.getElementById( this.getId() );
30056         
30057         var colElementsByName = document.getElementsByName( this.getId() ) ;
30058          
30059         oTextarea.style.display = 'none' ;
30060
30061         if ( oTextarea.tabIndex ) {            
30062             this.TabIndex = oTextarea.tabIndex ;
30063         }
30064         
30065         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30066         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30067         this.frame = Roo.get(this.getId() + '___Frame')
30068     },
30069     
30070     _getConfigHtml : function()
30071     {
30072         var sConfig = '' ;
30073
30074         for ( var o in this.fckconfig ) {
30075             sConfig += sConfig.length > 0  ? '&amp;' : '';
30076             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30077         }
30078
30079         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30080     },
30081     
30082     
30083     _getIFrameHtml : function()
30084     {
30085         var sFile = 'fckeditor.html' ;
30086         /* no idea what this is about..
30087         try
30088         {
30089             if ( (/fcksource=true/i).test( window.top.location.search ) )
30090                 sFile = 'fckeditor.original.html' ;
30091         }
30092         catch (e) { 
30093         */
30094
30095         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30096         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30097         
30098         
30099         var html = '<iframe id="' + this.getId() +
30100             '___Frame" src="' + sLink +
30101             '" width="' + this.width +
30102             '" height="' + this.height + '"' +
30103             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30104             ' frameborder="0" scrolling="no"></iframe>' ;
30105
30106         return html ;
30107     },
30108     
30109     _insertHtmlBefore : function( html, element )
30110     {
30111         if ( element.insertAdjacentHTML )       {
30112             // IE
30113             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30114         } else { // Gecko
30115             var oRange = document.createRange() ;
30116             oRange.setStartBefore( element ) ;
30117             var oFragment = oRange.createContextualFragment( html );
30118             element.parentNode.insertBefore( oFragment, element ) ;
30119         }
30120     }
30121     
30122     
30123   
30124     
30125     
30126     
30127     
30128
30129 });
30130
30131 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30132
30133 function FCKeditor_OnComplete(editorInstance){
30134     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30135     f.fckEditor = editorInstance;
30136     //console.log("loaded");
30137     f.fireEvent('editorinit', f, editorInstance);
30138
30139   
30140
30141  
30142
30143
30144
30145
30146
30147
30148
30149
30150
30151
30152
30153
30154
30155
30156
30157 //<script type="text/javascript">
30158 /**
30159  * @class Roo.form.GridField
30160  * @extends Roo.form.Field
30161  * Embed a grid (or editable grid into a form)
30162  * STATUS ALPHA
30163  * 
30164  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30165  * it needs 
30166  * xgrid.store = Roo.data.Store
30167  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30168  * xgrid.store.reader = Roo.data.JsonReader 
30169  * 
30170  * 
30171  * @constructor
30172  * Creates a new GridField
30173  * @param {Object} config Configuration options
30174  */
30175 Roo.form.GridField = function(config){
30176     Roo.form.GridField.superclass.constructor.call(this, config);
30177      
30178 };
30179
30180 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30181     /**
30182      * @cfg {Number} width  - used to restrict width of grid..
30183      */
30184     width : 100,
30185     /**
30186      * @cfg {Number} height - used to restrict height of grid..
30187      */
30188     height : 50,
30189      /**
30190      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30191          * 
30192          *}
30193      */
30194     xgrid : false, 
30195     /**
30196      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30197      * {tag: "input", type: "checkbox", autocomplete: "off"})
30198      */
30199    // defaultAutoCreate : { tag: 'div' },
30200     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30201     /**
30202      * @cfg {String} addTitle Text to include for adding a title.
30203      */
30204     addTitle : false,
30205     //
30206     onResize : function(){
30207         Roo.form.Field.superclass.onResize.apply(this, arguments);
30208     },
30209
30210     initEvents : function(){
30211         // Roo.form.Checkbox.superclass.initEvents.call(this);
30212         // has no events...
30213        
30214     },
30215
30216
30217     getResizeEl : function(){
30218         return this.wrap;
30219     },
30220
30221     getPositionEl : function(){
30222         return this.wrap;
30223     },
30224
30225     // private
30226     onRender : function(ct, position){
30227         
30228         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30229         var style = this.style;
30230         delete this.style;
30231         
30232         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30233         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30234         this.viewEl = this.wrap.createChild({ tag: 'div' });
30235         if (style) {
30236             this.viewEl.applyStyles(style);
30237         }
30238         if (this.width) {
30239             this.viewEl.setWidth(this.width);
30240         }
30241         if (this.height) {
30242             this.viewEl.setHeight(this.height);
30243         }
30244         //if(this.inputValue !== undefined){
30245         //this.setValue(this.value);
30246         
30247         
30248         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30249         
30250         
30251         this.grid.render();
30252         this.grid.getDataSource().on('remove', this.refreshValue, this);
30253         this.grid.getDataSource().on('update', this.refreshValue, this);
30254         this.grid.on('afteredit', this.refreshValue, this);
30255  
30256     },
30257      
30258     
30259     /**
30260      * Sets the value of the item. 
30261      * @param {String} either an object  or a string..
30262      */
30263     setValue : function(v){
30264         //this.value = v;
30265         v = v || []; // empty set..
30266         // this does not seem smart - it really only affects memoryproxy grids..
30267         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30268             var ds = this.grid.getDataSource();
30269             // assumes a json reader..
30270             var data = {}
30271             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30272             ds.loadData( data);
30273         }
30274         // clear selection so it does not get stale.
30275         if (this.grid.sm) { 
30276             this.grid.sm.clearSelections();
30277         }
30278         
30279         Roo.form.GridField.superclass.setValue.call(this, v);
30280         this.refreshValue();
30281         // should load data in the grid really....
30282     },
30283     
30284     // private
30285     refreshValue: function() {
30286          var val = [];
30287         this.grid.getDataSource().each(function(r) {
30288             val.push(r.data);
30289         });
30290         this.el.dom.value = Roo.encode(val);
30291     }
30292     
30293      
30294     
30295     
30296 });/*
30297  * Based on:
30298  * Ext JS Library 1.1.1
30299  * Copyright(c) 2006-2007, Ext JS, LLC.
30300  *
30301  * Originally Released Under LGPL - original licence link has changed is not relivant.
30302  *
30303  * Fork - LGPL
30304  * <script type="text/javascript">
30305  */
30306 /**
30307  * @class Roo.form.DisplayField
30308  * @extends Roo.form.Field
30309  * A generic Field to display non-editable data.
30310  * @constructor
30311  * Creates a new Display Field item.
30312  * @param {Object} config Configuration options
30313  */
30314 Roo.form.DisplayField = function(config){
30315     Roo.form.DisplayField.superclass.constructor.call(this, config);
30316     
30317 };
30318
30319 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30320     inputType:      'hidden',
30321     allowBlank:     true,
30322     readOnly:         true,
30323     
30324  
30325     /**
30326      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30327      */
30328     focusClass : undefined,
30329     /**
30330      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30331      */
30332     fieldClass: 'x-form-field',
30333     
30334      /**
30335      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30336      */
30337     valueRenderer: undefined,
30338     
30339     width: 100,
30340     /**
30341      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30342      * {tag: "input", type: "checkbox", autocomplete: "off"})
30343      */
30344      
30345  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30346
30347     onResize : function(){
30348         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30349         
30350     },
30351
30352     initEvents : function(){
30353         // Roo.form.Checkbox.superclass.initEvents.call(this);
30354         // has no events...
30355        
30356     },
30357
30358
30359     getResizeEl : function(){
30360         return this.wrap;
30361     },
30362
30363     getPositionEl : function(){
30364         return this.wrap;
30365     },
30366
30367     // private
30368     onRender : function(ct, position){
30369         
30370         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30371         //if(this.inputValue !== undefined){
30372         this.wrap = this.el.wrap();
30373         
30374         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30375         
30376         if (this.bodyStyle) {
30377             this.viewEl.applyStyles(this.bodyStyle);
30378         }
30379         //this.viewEl.setStyle('padding', '2px');
30380         
30381         this.setValue(this.value);
30382         
30383     },
30384 /*
30385     // private
30386     initValue : Roo.emptyFn,
30387
30388   */
30389
30390         // private
30391     onClick : function(){
30392         
30393     },
30394
30395     /**
30396      * Sets the checked state of the checkbox.
30397      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30398      */
30399     setValue : function(v){
30400         this.value = v;
30401         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30402         // this might be called before we have a dom element..
30403         if (!this.viewEl) {
30404             return;
30405         }
30406         this.viewEl.dom.innerHTML = html;
30407         Roo.form.DisplayField.superclass.setValue.call(this, v);
30408
30409     }
30410 });/*
30411  * 
30412  * Licence- LGPL
30413  * 
30414  */
30415
30416 /**
30417  * @class Roo.form.DayPicker
30418  * @extends Roo.form.Field
30419  * A Day picker show [M] [T] [W] ....
30420  * @constructor
30421  * Creates a new Day Picker
30422  * @param {Object} config Configuration options
30423  */
30424 Roo.form.DayPicker= function(config){
30425     Roo.form.DayPicker.superclass.constructor.call(this, config);
30426      
30427 };
30428
30429 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30430     /**
30431      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30432      */
30433     focusClass : undefined,
30434     /**
30435      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30436      */
30437     fieldClass: "x-form-field",
30438    
30439     /**
30440      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30441      * {tag: "input", type: "checkbox", autocomplete: "off"})
30442      */
30443     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30444     
30445    
30446     actionMode : 'viewEl', 
30447     //
30448     // private
30449  
30450     inputType : 'hidden',
30451     
30452      
30453     inputElement: false, // real input element?
30454     basedOn: false, // ????
30455     
30456     isFormField: true, // not sure where this is needed!!!!
30457
30458     onResize : function(){
30459         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30460         if(!this.boxLabel){
30461             this.el.alignTo(this.wrap, 'c-c');
30462         }
30463     },
30464
30465     initEvents : function(){
30466         Roo.form.Checkbox.superclass.initEvents.call(this);
30467         this.el.on("click", this.onClick,  this);
30468         this.el.on("change", this.onClick,  this);
30469     },
30470
30471
30472     getResizeEl : function(){
30473         return this.wrap;
30474     },
30475
30476     getPositionEl : function(){
30477         return this.wrap;
30478     },
30479
30480     
30481     // private
30482     onRender : function(ct, position){
30483         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30484        
30485         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30486         
30487         var r1 = '<table><tr>';
30488         var r2 = '<tr class="x-form-daypick-icons">';
30489         for (var i=0; i < 7; i++) {
30490             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30491             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30492         }
30493         
30494         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30495         viewEl.select('img').on('click', this.onClick, this);
30496         this.viewEl = viewEl;   
30497         
30498         
30499         // this will not work on Chrome!!!
30500         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30501         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30502         
30503         
30504           
30505
30506     },
30507
30508     // private
30509     initValue : Roo.emptyFn,
30510
30511     /**
30512      * Returns the checked state of the checkbox.
30513      * @return {Boolean} True if checked, else false
30514      */
30515     getValue : function(){
30516         return this.el.dom.value;
30517         
30518     },
30519
30520         // private
30521     onClick : function(e){ 
30522         //this.setChecked(!this.checked);
30523         Roo.get(e.target).toggleClass('x-menu-item-checked');
30524         this.refreshValue();
30525         //if(this.el.dom.checked != this.checked){
30526         //    this.setValue(this.el.dom.checked);
30527        // }
30528     },
30529     
30530     // private
30531     refreshValue : function()
30532     {
30533         var val = '';
30534         this.viewEl.select('img',true).each(function(e,i,n)  {
30535             val += e.is(".x-menu-item-checked") ? String(n) : '';
30536         });
30537         this.setValue(val, true);
30538     },
30539
30540     /**
30541      * Sets the checked state of the checkbox.
30542      * On is always based on a string comparison between inputValue and the param.
30543      * @param {Boolean/String} value - the value to set 
30544      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30545      */
30546     setValue : function(v,suppressEvent){
30547         if (!this.el.dom) {
30548             return;
30549         }
30550         var old = this.el.dom.value ;
30551         this.el.dom.value = v;
30552         if (suppressEvent) {
30553             return ;
30554         }
30555          
30556         // update display..
30557         this.viewEl.select('img',true).each(function(e,i,n)  {
30558             
30559             var on = e.is(".x-menu-item-checked");
30560             var newv = v.indexOf(String(n)) > -1;
30561             if (on != newv) {
30562                 e.toggleClass('x-menu-item-checked');
30563             }
30564             
30565         });
30566         
30567         
30568         this.fireEvent('change', this, v, old);
30569         
30570         
30571     },
30572    
30573     // handle setting of hidden value by some other method!!?!?
30574     setFromHidden: function()
30575     {
30576         if(!this.el){
30577             return;
30578         }
30579         //console.log("SET FROM HIDDEN");
30580         //alert('setFrom hidden');
30581         this.setValue(this.el.dom.value);
30582     },
30583     
30584     onDestroy : function()
30585     {
30586         if(this.viewEl){
30587             Roo.get(this.viewEl).remove();
30588         }
30589          
30590         Roo.form.DayPicker.superclass.onDestroy.call(this);
30591     }
30592
30593 });/*
30594  * RooJS Library 1.1.1
30595  * Copyright(c) 2008-2011  Alan Knowles
30596  *
30597  * License - LGPL
30598  */
30599  
30600
30601 /**
30602  * @class Roo.form.ComboCheck
30603  * @extends Roo.form.ComboBox
30604  * A combobox for multiple select items.
30605  *
30606  * FIXME - could do with a reset button..
30607  * 
30608  * @constructor
30609  * Create a new ComboCheck
30610  * @param {Object} config Configuration options
30611  */
30612 Roo.form.ComboCheck = function(config){
30613     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30614     // should verify some data...
30615     // like
30616     // hiddenName = required..
30617     // displayField = required
30618     // valudField == required
30619     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30620     var _t = this;
30621     Roo.each(req, function(e) {
30622         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30623             throw "Roo.form.ComboCheck : missing value for: " + e;
30624         }
30625     });
30626     
30627     
30628 };
30629
30630 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30631      
30632      
30633     editable : false,
30634      
30635     selectedClass: 'x-menu-item-checked', 
30636     
30637     // private
30638     onRender : function(ct, position){
30639         var _t = this;
30640         
30641         
30642         
30643         if(!this.tpl){
30644             var cls = 'x-combo-list';
30645
30646             
30647             this.tpl =  new Roo.Template({
30648                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30649                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30650                    '<span>{' + this.displayField + '}</span>' +
30651                     '</div>' 
30652                 
30653             });
30654         }
30655  
30656         
30657         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30658         this.view.singleSelect = false;
30659         this.view.multiSelect = true;
30660         this.view.toggleSelect = true;
30661         this.pageTb.add(new Roo.Toolbar.Fill(), {
30662             
30663             text: 'Done',
30664             handler: function()
30665             {
30666                 _t.collapse();
30667             }
30668         });
30669     },
30670     
30671     onViewOver : function(e, t){
30672         // do nothing...
30673         return;
30674         
30675     },
30676     
30677     onViewClick : function(doFocus,index){
30678         return;
30679         
30680     },
30681     select: function () {
30682         //Roo.log("SELECT CALLED");
30683     },
30684      
30685     selectByValue : function(xv, scrollIntoView){
30686         var ar = this.getValueArray();
30687         var sels = [];
30688         
30689         Roo.each(ar, function(v) {
30690             if(v === undefined || v === null){
30691                 return;
30692             }
30693             var r = this.findRecord(this.valueField, v);
30694             if(r){
30695                 sels.push(this.store.indexOf(r))
30696                 
30697             }
30698         },this);
30699         this.view.select(sels);
30700         return false;
30701     },
30702     
30703     
30704     
30705     onSelect : function(record, index){
30706        // Roo.log("onselect Called");
30707        // this is only called by the clear button now..
30708         this.view.clearSelections();
30709         this.setValue('[]');
30710         if (this.value != this.valueBefore) {
30711             this.fireEvent('change', this, this.value, this.valueBefore);
30712             this.valueBefore = this.value;
30713         }
30714     },
30715     getValueArray : function()
30716     {
30717         var ar = [] ;
30718         
30719         try {
30720             //Roo.log(this.value);
30721             if (typeof(this.value) == 'undefined') {
30722                 return [];
30723             }
30724             var ar = Roo.decode(this.value);
30725             return  ar instanceof Array ? ar : []; //?? valid?
30726             
30727         } catch(e) {
30728             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30729             return [];
30730         }
30731          
30732     },
30733     expand : function ()
30734     {
30735         
30736         Roo.form.ComboCheck.superclass.expand.call(this);
30737         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30738         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30739         
30740
30741     },
30742     
30743     collapse : function(){
30744         Roo.form.ComboCheck.superclass.collapse.call(this);
30745         var sl = this.view.getSelectedIndexes();
30746         var st = this.store;
30747         var nv = [];
30748         var tv = [];
30749         var r;
30750         Roo.each(sl, function(i) {
30751             r = st.getAt(i);
30752             nv.push(r.get(this.valueField));
30753         },this);
30754         this.setValue(Roo.encode(nv));
30755         if (this.value != this.valueBefore) {
30756
30757             this.fireEvent('change', this, this.value, this.valueBefore);
30758             this.valueBefore = this.value;
30759         }
30760         
30761     },
30762     
30763     setValue : function(v){
30764         // Roo.log(v);
30765         this.value = v;
30766         
30767         var vals = this.getValueArray();
30768         var tv = [];
30769         Roo.each(vals, function(k) {
30770             var r = this.findRecord(this.valueField, k);
30771             if(r){
30772                 tv.push(r.data[this.displayField]);
30773             }else if(this.valueNotFoundText !== undefined){
30774                 tv.push( this.valueNotFoundText );
30775             }
30776         },this);
30777        // Roo.log(tv);
30778         
30779         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30780         this.hiddenField.value = v;
30781         this.value = v;
30782     }
30783     
30784 });/*
30785  * Based on:
30786  * Ext JS Library 1.1.1
30787  * Copyright(c) 2006-2007, Ext JS, LLC.
30788  *
30789  * Originally Released Under LGPL - original licence link has changed is not relivant.
30790  *
30791  * Fork - LGPL
30792  * <script type="text/javascript">
30793  */
30794  
30795 /**
30796  * @class Roo.form.Signature
30797  * @extends Roo.form.Field
30798  * Signature field.  
30799  * @constructor
30800  * 
30801  * @param {Object} config Configuration options
30802  */
30803
30804 Roo.form.Signature = function(config){
30805     Roo.form.Signature.superclass.constructor.call(this, config);
30806     
30807     this.addEvents({// not in used??
30808          /**
30809          * @event confirm
30810          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30811              * @param {Roo.form.Signature} combo This combo box
30812              */
30813         'confirm' : true,
30814         /**
30815          * @event reset
30816          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30817              * @param {Roo.form.ComboBox} combo This combo box
30818              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30819              */
30820         'reset' : true
30821     });
30822 };
30823
30824 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
30825     /**
30826      * @cfg {Object} labels Label to use when rendering a form.
30827      * defaults to 
30828      * labels : { 
30829      *      clear : "Clear",
30830      *      confirm : "Confirm"
30831      *  }
30832      */
30833     labels : { 
30834         clear : "Clear",
30835         confirm : "Confirm"
30836     },
30837     /**
30838      * @cfg {Number} width The signature panel width (defaults to 300)
30839      */
30840     width: 300,
30841     /**
30842      * @cfg {Number} height The signature panel height (defaults to 100)
30843      */
30844     height : 100,
30845     /**
30846      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30847      */
30848     allowBlank : false,
30849     
30850     //private
30851     // {Object} signPanel The signature SVG panel element (defaults to {})
30852     signPanel : {},
30853     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30854     isMouseDown : false,
30855     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30856     isConfirmed : false,
30857     // {String} signatureTmp SVG mapping string (defaults to empty string)
30858     signatureTmp : '',
30859     
30860     
30861     defaultAutoCreate : { // modified by initCompnoent..
30862         tag: "input",
30863         type:"hidden"
30864     },
30865
30866     // private
30867     onRender : function(ct, position){
30868         
30869         Roo.form.Signature.superclass.onRender.call(this, ct, position);
30870         
30871         this.wrap = this.el.wrap({
30872             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
30873         });
30874         
30875         this.createToolbar(this);
30876         this.signPanel = this.wrap.createChild({
30877                 tag: 'div',
30878                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
30879             }, this.el
30880         );
30881             
30882         this.svgID = Roo.id();
30883         this.svgEl = this.signPanel.createChild({
30884               xmlns : 'http://www.w3.org/2000/svg',
30885               tag : 'svg',
30886               id : this.svgID + "-svg",
30887               width: this.width,
30888               height: this.height,
30889               viewBox: '0 0 '+this.width+' '+this.height,
30890               cn : [
30891                 {
30892                     tag: "rect",
30893                     id: this.svgID + "-svg-r",
30894                     width: this.width,
30895                     height: this.height,
30896                     fill: "#ffa"
30897                 },
30898                 {
30899                     tag: "line",
30900                     id: this.svgID + "-svg-l",
30901                     x1: "0", // start
30902                     y1: (this.height*0.8), // start set the line in 80% of height
30903                     x2: this.width, // end
30904                     y2: (this.height*0.8), // end set the line in 80% of height
30905                     'stroke': "#666",
30906                     'stroke-width': "1",
30907                     'stroke-dasharray': "3",
30908                     'shape-rendering': "crispEdges",
30909                     'pointer-events': "none"
30910                 },
30911                 {
30912                     tag: "path",
30913                     id: this.svgID + "-svg-p",
30914                     'stroke': "navy",
30915                     'stroke-width': "3",
30916                     'fill': "none",
30917                     'pointer-events': 'none'
30918                 }
30919               ]
30920         });
30921         this.createSVG();
30922         this.svgBox = this.svgEl.dom.getScreenCTM();
30923     },
30924     createSVG : function(){ 
30925         var svg = this.signPanel;
30926         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
30927         var t = this;
30928
30929         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
30930         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
30931         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
30932         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
30933         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
30934         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
30935         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
30936         
30937     },
30938     isTouchEvent : function(e){
30939         return e.type.match(/^touch/);
30940     },
30941     getCoords : function (e) {
30942         var pt    = this.svgEl.dom.createSVGPoint();
30943         pt.x = e.clientX; 
30944         pt.y = e.clientY;
30945         if (this.isTouchEvent(e)) {
30946             pt.x =  e.targetTouches[0].clientX 
30947             pt.y = e.targetTouches[0].clientY;
30948         }
30949         var a = this.svgEl.dom.getScreenCTM();
30950         var b = a.inverse();
30951         var mx = pt.matrixTransform(b);
30952         return mx.x + ',' + mx.y;
30953     },
30954     //mouse event headler 
30955     down : function (e) {
30956         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
30957         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
30958         
30959         this.isMouseDown = true;
30960         
30961         e.preventDefault();
30962     },
30963     move : function (e) {
30964         if (this.isMouseDown) {
30965             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
30966             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
30967         }
30968         
30969         e.preventDefault();
30970     },
30971     up : function (e) {
30972         this.isMouseDown = false;
30973         var sp = this.signatureTmp.split(' ');
30974         
30975         if(sp.length > 1){
30976             if(!sp[sp.length-2].match(/^L/)){
30977                 sp.pop();
30978                 sp.pop();
30979                 sp.push("");
30980                 this.signatureTmp = sp.join(" ");
30981             }
30982         }
30983         if(this.getValue() != this.signatureTmp){
30984             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
30985             this.isConfirmed = false;
30986         }
30987         e.preventDefault();
30988     },
30989     
30990     /**
30991      * Protected method that will not generally be called directly. It
30992      * is called when the editor creates its toolbar. Override this method if you need to
30993      * add custom toolbar buttons.
30994      * @param {HtmlEditor} editor
30995      */
30996     createToolbar : function(editor){
30997          function btn(id, toggle, handler){
30998             var xid = fid + '-'+ id ;
30999             return {
31000                 id : xid,
31001                 cmd : id,
31002                 cls : 'x-btn-icon x-edit-'+id,
31003                 enableToggle:toggle !== false,
31004                 scope: editor, // was editor...
31005                 handler:handler||editor.relayBtnCmd,
31006                 clickEvent:'mousedown',
31007                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31008                 tabIndex:-1
31009             };
31010         }
31011         
31012         
31013         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31014         this.tb = tb;
31015         this.tb.add(
31016            {
31017                 cls : ' x-signature-btn x-signature-'+id,
31018                 scope: editor, // was editor...
31019                 handler: this.reset,
31020                 clickEvent:'mousedown',
31021                 text: this.labels.clear
31022             },
31023             {
31024                  xtype : 'Fill',
31025                  xns: Roo.Toolbar
31026             }, 
31027             {
31028                 cls : '  x-signature-btn x-signature-'+id,
31029                 scope: editor, // was editor...
31030                 handler: this.confirmHandler,
31031                 clickEvent:'mousedown',
31032                 text: this.labels.confirm
31033             }
31034         );
31035     
31036     },
31037     //public
31038     /**
31039      * when user is clicked confirm then show this image.....
31040      * 
31041      * @return {String} Image Data URI
31042      */
31043     getImageDataURI : function(){
31044         var svg = this.svgEl.dom.parentNode.innerHTML;
31045         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31046         return src; 
31047     },
31048     /**
31049      * 
31050      * @return {Boolean} this.isConfirmed
31051      */
31052     getConfirmed : function(){
31053         return this.isConfirmed;
31054     },
31055     /**
31056      * 
31057      * @return {Number} this.width
31058      */
31059     getWidth : function(){
31060         return this.width;
31061     },
31062     /**
31063      * 
31064      * @return {Number} this.height
31065      */
31066     getHeight : function(){
31067         return this.height;
31068     },
31069     // private
31070     getSignature : function(){
31071         return this.signatureTmp;
31072     },
31073     // private
31074     reset : function(){
31075         this.signatureTmp = '';
31076         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31077         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31078         this.isConfirmed = false;
31079         Roo.form.Signature.superclass.reset.call(this);
31080     },
31081     setSignature : function(s){
31082         this.signatureTmp = s;
31083         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31084         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31085         this.setValue(s);
31086         this.isConfirmed = false;
31087         Roo.form.Signature.superclass.reset.call(this);
31088     }, 
31089     test : function(){
31090 //        Roo.log(this.signPanel.dom.contentWindow.up())
31091     },
31092     //private
31093     setConfirmed : function(){
31094         
31095         
31096         
31097 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31098     },
31099     // private
31100     confirmHandler : function(){
31101         if(!this.getSignature()){
31102             return;
31103         }
31104         
31105         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31106         this.setValue(this.getSignature());
31107         this.isConfirmed = true;
31108         
31109         this.fireEvent('confirm', this);
31110     },
31111     // private
31112     // Subclasses should provide the validation implementation by overriding this
31113     validateValue : function(value){
31114         if(this.allowBlank){
31115             return true;
31116         }
31117         
31118         if(this.isConfirmed){
31119             return true;
31120         }
31121         return false;
31122     }
31123 });/*
31124  * Based on:
31125  * Ext JS Library 1.1.1
31126  * Copyright(c) 2006-2007, Ext JS, LLC.
31127  *
31128  * Originally Released Under LGPL - original licence link has changed is not relivant.
31129  *
31130  * Fork - LGPL
31131  * <script type="text/javascript">
31132  */
31133  
31134
31135 /**
31136  * @class Roo.form.ComboBox
31137  * @extends Roo.form.TriggerField
31138  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31139  * @constructor
31140  * Create a new ComboBox.
31141  * @param {Object} config Configuration options
31142  */
31143 Roo.form.Select = function(config){
31144     Roo.form.Select.superclass.constructor.call(this, config);
31145      
31146 };
31147
31148 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31149     /**
31150      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31151      */
31152     /**
31153      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31154      * rendering into an Roo.Editor, defaults to false)
31155      */
31156     /**
31157      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31158      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31159      */
31160     /**
31161      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31162      */
31163     /**
31164      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31165      * the dropdown list (defaults to undefined, with no header element)
31166      */
31167
31168      /**
31169      * @cfg {String/Roo.Template} tpl The template to use to render the output
31170      */
31171      
31172     // private
31173     defaultAutoCreate : {tag: "select"  },
31174     /**
31175      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31176      */
31177     listWidth: undefined,
31178     /**
31179      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31180      * mode = 'remote' or 'text' if mode = 'local')
31181      */
31182     displayField: undefined,
31183     /**
31184      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31185      * mode = 'remote' or 'value' if mode = 'local'). 
31186      * Note: use of a valueField requires the user make a selection
31187      * in order for a value to be mapped.
31188      */
31189     valueField: undefined,
31190     
31191     
31192     /**
31193      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31194      * field's data value (defaults to the underlying DOM element's name)
31195      */
31196     hiddenName: undefined,
31197     /**
31198      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31199      */
31200     listClass: '',
31201     /**
31202      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31203      */
31204     selectedClass: 'x-combo-selected',
31205     /**
31206      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31207      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31208      * which displays a downward arrow icon).
31209      */
31210     triggerClass : 'x-form-arrow-trigger',
31211     /**
31212      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31213      */
31214     shadow:'sides',
31215     /**
31216      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31217      * anchor positions (defaults to 'tl-bl')
31218      */
31219     listAlign: 'tl-bl?',
31220     /**
31221      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31222      */
31223     maxHeight: 300,
31224     /**
31225      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31226      * query specified by the allQuery config option (defaults to 'query')
31227      */
31228     triggerAction: 'query',
31229     /**
31230      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31231      * (defaults to 4, does not apply if editable = false)
31232      */
31233     minChars : 4,
31234     /**
31235      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31236      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31237      */
31238     typeAhead: false,
31239     /**
31240      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31241      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31242      */
31243     queryDelay: 500,
31244     /**
31245      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31246      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31247      */
31248     pageSize: 0,
31249     /**
31250      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31251      * when editable = true (defaults to false)
31252      */
31253     selectOnFocus:false,
31254     /**
31255      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31256      */
31257     queryParam: 'query',
31258     /**
31259      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31260      * when mode = 'remote' (defaults to 'Loading...')
31261      */
31262     loadingText: 'Loading...',
31263     /**
31264      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31265      */
31266     resizable: false,
31267     /**
31268      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31269      */
31270     handleHeight : 8,
31271     /**
31272      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31273      * traditional select (defaults to true)
31274      */
31275     editable: true,
31276     /**
31277      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31278      */
31279     allQuery: '',
31280     /**
31281      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31282      */
31283     mode: 'remote',
31284     /**
31285      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31286      * listWidth has a higher value)
31287      */
31288     minListWidth : 70,
31289     /**
31290      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31291      * allow the user to set arbitrary text into the field (defaults to false)
31292      */
31293     forceSelection:false,
31294     /**
31295      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31296      * if typeAhead = true (defaults to 250)
31297      */
31298     typeAheadDelay : 250,
31299     /**
31300      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31301      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31302      */
31303     valueNotFoundText : undefined,
31304     
31305     /**
31306      * @cfg {String} defaultValue The value displayed after loading the store.
31307      */
31308     defaultValue: '',
31309     
31310     /**
31311      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31312      */
31313     blockFocus : false,
31314     
31315     /**
31316      * @cfg {Boolean} disableClear Disable showing of clear button.
31317      */
31318     disableClear : false,
31319     /**
31320      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31321      */
31322     alwaysQuery : false,
31323     
31324     //private
31325     addicon : false,
31326     editicon: false,
31327     
31328     // element that contains real text value.. (when hidden is used..)
31329      
31330     // private
31331     onRender : function(ct, position){
31332         Roo.form.Field.prototype.onRender.call(this, ct, position);
31333         
31334         if(this.store){
31335             this.store.on('beforeload', this.onBeforeLoad, this);
31336             this.store.on('load', this.onLoad, this);
31337             this.store.on('loadexception', this.onLoadException, this);
31338             this.store.load({});
31339         }
31340         
31341         
31342         
31343     },
31344
31345     // private
31346     initEvents : function(){
31347         //Roo.form.ComboBox.superclass.initEvents.call(this);
31348  
31349     },
31350
31351     onDestroy : function(){
31352        
31353         if(this.store){
31354             this.store.un('beforeload', this.onBeforeLoad, this);
31355             this.store.un('load', this.onLoad, this);
31356             this.store.un('loadexception', this.onLoadException, this);
31357         }
31358         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31359     },
31360
31361     // private
31362     fireKey : function(e){
31363         if(e.isNavKeyPress() && !this.list.isVisible()){
31364             this.fireEvent("specialkey", this, e);
31365         }
31366     },
31367
31368     // private
31369     onResize: function(w, h){
31370         
31371         return; 
31372     
31373         
31374     },
31375
31376     /**
31377      * Allow or prevent the user from directly editing the field text.  If false is passed,
31378      * the user will only be able to select from the items defined in the dropdown list.  This method
31379      * is the runtime equivalent of setting the 'editable' config option at config time.
31380      * @param {Boolean} value True to allow the user to directly edit the field text
31381      */
31382     setEditable : function(value){
31383          
31384     },
31385
31386     // private
31387     onBeforeLoad : function(){
31388         
31389         Roo.log("Select before load");
31390         return;
31391     
31392         this.innerList.update(this.loadingText ?
31393                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31394         //this.restrictHeight();
31395         this.selectedIndex = -1;
31396     },
31397
31398     // private
31399     onLoad : function(){
31400
31401     
31402         var dom = this.el.dom;
31403         dom.innerHTML = '';
31404          var od = dom.ownerDocument;
31405          
31406         if (this.emptyText) {
31407             var op = od.createElement('option');
31408             op.setAttribute('value', '');
31409             op.innerHTML = String.format('{0}', this.emptyText);
31410             dom.appendChild(op);
31411         }
31412         if(this.store.getCount() > 0){
31413            
31414             var vf = this.valueField;
31415             var df = this.displayField;
31416             this.store.data.each(function(r) {
31417                 // which colmsn to use... testing - cdoe / title..
31418                 var op = od.createElement('option');
31419                 op.setAttribute('value', r.data[vf]);
31420                 op.innerHTML = String.format('{0}', r.data[df]);
31421                 dom.appendChild(op);
31422             });
31423             if (typeof(this.defaultValue != 'undefined')) {
31424                 this.setValue(this.defaultValue);
31425             }
31426             
31427              
31428         }else{
31429             //this.onEmptyResults();
31430         }
31431         //this.el.focus();
31432     },
31433     // private
31434     onLoadException : function()
31435     {
31436         dom.innerHTML = '';
31437             
31438         Roo.log("Select on load exception");
31439         return;
31440     
31441         this.collapse();
31442         Roo.log(this.store.reader.jsonData);
31443         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31444             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31445         }
31446         
31447         
31448     },
31449     // private
31450     onTypeAhead : function(){
31451          
31452     },
31453
31454     // private
31455     onSelect : function(record, index){
31456         Roo.log('on select?');
31457         return;
31458         if(this.fireEvent('beforeselect', this, record, index) !== false){
31459             this.setFromData(index > -1 ? record.data : false);
31460             this.collapse();
31461             this.fireEvent('select', this, record, index);
31462         }
31463     },
31464
31465     /**
31466      * Returns the currently selected field value or empty string if no value is set.
31467      * @return {String} value The selected value
31468      */
31469     getValue : function(){
31470         var dom = this.el.dom;
31471         this.value = dom.options[dom.selectedIndex].value;
31472         return this.value;
31473         
31474     },
31475
31476     /**
31477      * Clears any text/value currently set in the field
31478      */
31479     clearValue : function(){
31480         this.value = '';
31481         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31482         
31483     },
31484
31485     /**
31486      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31487      * will be displayed in the field.  If the value does not match the data value of an existing item,
31488      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31489      * Otherwise the field will be blank (although the value will still be set).
31490      * @param {String} value The value to match
31491      */
31492     setValue : function(v){
31493         var d = this.el.dom;
31494         for (var i =0; i < d.options.length;i++) {
31495             if (v == d.options[i].value) {
31496                 d.selectedIndex = i;
31497                 this.value = v;
31498                 return;
31499             }
31500         }
31501         this.clearValue();
31502     },
31503     /**
31504      * @property {Object} the last set data for the element
31505      */
31506     
31507     lastData : false,
31508     /**
31509      * Sets the value of the field based on a object which is related to the record format for the store.
31510      * @param {Object} value the value to set as. or false on reset?
31511      */
31512     setFromData : function(o){
31513         Roo.log('setfrom data?');
31514          
31515         
31516         
31517     },
31518     // private
31519     reset : function(){
31520         this.clearValue();
31521     },
31522     // private
31523     findRecord : function(prop, value){
31524         
31525         return false;
31526     
31527         var record;
31528         if(this.store.getCount() > 0){
31529             this.store.each(function(r){
31530                 if(r.data[prop] == value){
31531                     record = r;
31532                     return false;
31533                 }
31534                 return true;
31535             });
31536         }
31537         return record;
31538     },
31539     
31540     getName: function()
31541     {
31542         // returns hidden if it's set..
31543         if (!this.rendered) {return ''};
31544         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
31545         
31546     },
31547      
31548
31549     
31550
31551     // private
31552     onEmptyResults : function(){
31553         Roo.log('empty results');
31554         //this.collapse();
31555     },
31556
31557     /**
31558      * Returns true if the dropdown list is expanded, else false.
31559      */
31560     isExpanded : function(){
31561         return false;
31562     },
31563
31564     /**
31565      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31566      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31567      * @param {String} value The data value of the item to select
31568      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31569      * selected item if it is not currently in view (defaults to true)
31570      * @return {Boolean} True if the value matched an item in the list, else false
31571      */
31572     selectByValue : function(v, scrollIntoView){
31573         Roo.log('select By Value');
31574         return false;
31575     
31576         if(v !== undefined && v !== null){
31577             var r = this.findRecord(this.valueField || this.displayField, v);
31578             if(r){
31579                 this.select(this.store.indexOf(r), scrollIntoView);
31580                 return true;
31581             }
31582         }
31583         return false;
31584     },
31585
31586     /**
31587      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31588      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31589      * @param {Number} index The zero-based index of the list item to select
31590      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31591      * selected item if it is not currently in view (defaults to true)
31592      */
31593     select : function(index, scrollIntoView){
31594         Roo.log('select ');
31595         return  ;
31596         
31597         this.selectedIndex = index;
31598         this.view.select(index);
31599         if(scrollIntoView !== false){
31600             var el = this.view.getNode(index);
31601             if(el){
31602                 this.innerList.scrollChildIntoView(el, false);
31603             }
31604         }
31605     },
31606
31607       
31608
31609     // private
31610     validateBlur : function(){
31611         
31612         return;
31613         
31614     },
31615
31616     // private
31617     initQuery : function(){
31618         this.doQuery(this.getRawValue());
31619     },
31620
31621     // private
31622     doForce : function(){
31623         if(this.el.dom.value.length > 0){
31624             this.el.dom.value =
31625                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31626              
31627         }
31628     },
31629
31630     /**
31631      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
31632      * query allowing the query action to be canceled if needed.
31633      * @param {String} query The SQL query to execute
31634      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31635      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
31636      * saved in the current store (defaults to false)
31637      */
31638     doQuery : function(q, forceAll){
31639         
31640         Roo.log('doQuery?');
31641         if(q === undefined || q === null){
31642             q = '';
31643         }
31644         var qe = {
31645             query: q,
31646             forceAll: forceAll,
31647             combo: this,
31648             cancel:false
31649         };
31650         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31651             return false;
31652         }
31653         q = qe.query;
31654         forceAll = qe.forceAll;
31655         if(forceAll === true || (q.length >= this.minChars)){
31656             if(this.lastQuery != q || this.alwaysQuery){
31657                 this.lastQuery = q;
31658                 if(this.mode == 'local'){
31659                     this.selectedIndex = -1;
31660                     if(forceAll){
31661                         this.store.clearFilter();
31662                     }else{
31663                         this.store.filter(this.displayField, q);
31664                     }
31665                     this.onLoad();
31666                 }else{
31667                     this.store.baseParams[this.queryParam] = q;
31668                     this.store.load({
31669                         params: this.getParams(q)
31670                     });
31671                     this.expand();
31672                 }
31673             }else{
31674                 this.selectedIndex = -1;
31675                 this.onLoad();   
31676             }
31677         }
31678     },
31679
31680     // private
31681     getParams : function(q){
31682         var p = {};
31683         //p[this.queryParam] = q;
31684         if(this.pageSize){
31685             p.start = 0;
31686             p.limit = this.pageSize;
31687         }
31688         return p;
31689     },
31690
31691     /**
31692      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31693      */
31694     collapse : function(){
31695         
31696     },
31697
31698     // private
31699     collapseIf : function(e){
31700         
31701     },
31702
31703     /**
31704      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31705      */
31706     expand : function(){
31707         
31708     } ,
31709
31710     // private
31711      
31712
31713     /** 
31714     * @cfg {Boolean} grow 
31715     * @hide 
31716     */
31717     /** 
31718     * @cfg {Number} growMin 
31719     * @hide 
31720     */
31721     /** 
31722     * @cfg {Number} growMax 
31723     * @hide 
31724     */
31725     /**
31726      * @hide
31727      * @method autoSize
31728      */
31729     
31730     setWidth : function()
31731     {
31732         
31733     },
31734     getResizeEl : function(){
31735         return this.el;
31736     }
31737 });//<script type="text/javasscript">
31738  
31739
31740 /**
31741  * @class Roo.DDView
31742  * A DnD enabled version of Roo.View.
31743  * @param {Element/String} container The Element in which to create the View.
31744  * @param {String} tpl The template string used to create the markup for each element of the View
31745  * @param {Object} config The configuration properties. These include all the config options of
31746  * {@link Roo.View} plus some specific to this class.<br>
31747  * <p>
31748  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31749  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31750  * <p>
31751  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31752 .x-view-drag-insert-above {
31753         border-top:1px dotted #3366cc;
31754 }
31755 .x-view-drag-insert-below {
31756         border-bottom:1px dotted #3366cc;
31757 }
31758 </code></pre>
31759  * 
31760  */
31761  
31762 Roo.DDView = function(container, tpl, config) {
31763     Roo.DDView.superclass.constructor.apply(this, arguments);
31764     this.getEl().setStyle("outline", "0px none");
31765     this.getEl().unselectable();
31766     if (this.dragGroup) {
31767                 this.setDraggable(this.dragGroup.split(","));
31768     }
31769     if (this.dropGroup) {
31770                 this.setDroppable(this.dropGroup.split(","));
31771     }
31772     if (this.deletable) {
31773         this.setDeletable();
31774     }
31775     this.isDirtyFlag = false;
31776         this.addEvents({
31777                 "drop" : true
31778         });
31779 };
31780
31781 Roo.extend(Roo.DDView, Roo.View, {
31782 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31783 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31784 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31785 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31786
31787         isFormField: true,
31788
31789         reset: Roo.emptyFn,
31790         
31791         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31792
31793         validate: function() {
31794                 return true;
31795         },
31796         
31797         destroy: function() {
31798                 this.purgeListeners();
31799                 this.getEl.removeAllListeners();
31800                 this.getEl().remove();
31801                 if (this.dragZone) {
31802                         if (this.dragZone.destroy) {
31803                                 this.dragZone.destroy();
31804                         }
31805                 }
31806                 if (this.dropZone) {
31807                         if (this.dropZone.destroy) {
31808                                 this.dropZone.destroy();
31809                         }
31810                 }
31811         },
31812
31813 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31814         getName: function() {
31815                 return this.name;
31816         },
31817
31818 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31819         setValue: function(v) {
31820                 if (!this.store) {
31821                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
31822                 }
31823                 var data = {};
31824                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31825                 this.store.proxy = new Roo.data.MemoryProxy(data);
31826                 this.store.load();
31827         },
31828
31829 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
31830         getValue: function() {
31831                 var result = '(';
31832                 this.store.each(function(rec) {
31833                         result += rec.id + ',';
31834                 });
31835                 return result.substr(0, result.length - 1) + ')';
31836         },
31837         
31838         getIds: function() {
31839                 var i = 0, result = new Array(this.store.getCount());
31840                 this.store.each(function(rec) {
31841                         result[i++] = rec.id;
31842                 });
31843                 return result;
31844         },
31845         
31846         isDirty: function() {
31847                 return this.isDirtyFlag;
31848         },
31849
31850 /**
31851  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
31852  *      whole Element becomes the target, and this causes the drop gesture to append.
31853  */
31854     getTargetFromEvent : function(e) {
31855                 var target = e.getTarget();
31856                 while ((target !== null) && (target.parentNode != this.el.dom)) {
31857                 target = target.parentNode;
31858                 }
31859                 if (!target) {
31860                         target = this.el.dom.lastChild || this.el.dom;
31861                 }
31862                 return target;
31863     },
31864
31865 /**
31866  *      Create the drag data which consists of an object which has the property "ddel" as
31867  *      the drag proxy element. 
31868  */
31869     getDragData : function(e) {
31870         var target = this.findItemFromChild(e.getTarget());
31871                 if(target) {
31872                         this.handleSelection(e);
31873                         var selNodes = this.getSelectedNodes();
31874             var dragData = {
31875                 source: this,
31876                 copy: this.copy || (this.allowCopy && e.ctrlKey),
31877                 nodes: selNodes,
31878                 records: []
31879                         };
31880                         var selectedIndices = this.getSelectedIndexes();
31881                         for (var i = 0; i < selectedIndices.length; i++) {
31882                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
31883                         }
31884                         if (selNodes.length == 1) {
31885                                 dragData.ddel = target.cloneNode(true); // the div element
31886                         } else {
31887                                 var div = document.createElement('div'); // create the multi element drag "ghost"
31888                                 div.className = 'multi-proxy';
31889                                 for (var i = 0, len = selNodes.length; i < len; i++) {
31890                                         div.appendChild(selNodes[i].cloneNode(true));
31891                                 }
31892                                 dragData.ddel = div;
31893                         }
31894             //console.log(dragData)
31895             //console.log(dragData.ddel.innerHTML)
31896                         return dragData;
31897                 }
31898         //console.log('nodragData')
31899                 return false;
31900     },
31901     
31902 /**     Specify to which ddGroup items in this DDView may be dragged. */
31903     setDraggable: function(ddGroup) {
31904         if (ddGroup instanceof Array) {
31905                 Roo.each(ddGroup, this.setDraggable, this);
31906                 return;
31907         }
31908         if (this.dragZone) {
31909                 this.dragZone.addToGroup(ddGroup);
31910         } else {
31911                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
31912                                 containerScroll: true,
31913                                 ddGroup: ddGroup 
31914
31915                         });
31916 //                      Draggability implies selection. DragZone's mousedown selects the element.
31917                         if (!this.multiSelect) { this.singleSelect = true; }
31918
31919 //                      Wire the DragZone's handlers up to methods in *this*
31920                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
31921                 }
31922     },
31923
31924 /**     Specify from which ddGroup this DDView accepts drops. */
31925     setDroppable: function(ddGroup) {
31926         if (ddGroup instanceof Array) {
31927                 Roo.each(ddGroup, this.setDroppable, this);
31928                 return;
31929         }
31930         if (this.dropZone) {
31931                 this.dropZone.addToGroup(ddGroup);
31932         } else {
31933                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
31934                                 containerScroll: true,
31935                                 ddGroup: ddGroup
31936                         });
31937
31938 //                      Wire the DropZone's handlers up to methods in *this*
31939                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
31940                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
31941                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
31942                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
31943                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
31944                 }
31945     },
31946
31947 /**     Decide whether to drop above or below a View node. */
31948     getDropPoint : function(e, n, dd){
31949         if (n == this.el.dom) { return "above"; }
31950                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
31951                 var c = t + (b - t) / 2;
31952                 var y = Roo.lib.Event.getPageY(e);
31953                 if(y <= c) {
31954                         return "above";
31955                 }else{
31956                         return "below";
31957                 }
31958     },
31959
31960     onNodeEnter : function(n, dd, e, data){
31961                 return false;
31962     },
31963     
31964     onNodeOver : function(n, dd, e, data){
31965                 var pt = this.getDropPoint(e, n, dd);
31966                 // set the insert point style on the target node
31967                 var dragElClass = this.dropNotAllowed;
31968                 if (pt) {
31969                         var targetElClass;
31970                         if (pt == "above"){
31971                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
31972                                 targetElClass = "x-view-drag-insert-above";
31973                         } else {
31974                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
31975                                 targetElClass = "x-view-drag-insert-below";
31976                         }
31977                         if (this.lastInsertClass != targetElClass){
31978                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
31979                                 this.lastInsertClass = targetElClass;
31980                         }
31981                 }
31982                 return dragElClass;
31983         },
31984
31985     onNodeOut : function(n, dd, e, data){
31986                 this.removeDropIndicators(n);
31987     },
31988
31989     onNodeDrop : function(n, dd, e, data){
31990         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
31991                 return false;
31992         }
31993         var pt = this.getDropPoint(e, n, dd);
31994                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
31995                 if (pt == "below") { insertAt++; }
31996                 for (var i = 0; i < data.records.length; i++) {
31997                         var r = data.records[i];
31998                         var dup = this.store.getById(r.id);
31999                         if (dup && (dd != this.dragZone)) {
32000                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32001                         } else {
32002                                 if (data.copy) {
32003                                         this.store.insert(insertAt++, r.copy());
32004                                 } else {
32005                                         data.source.isDirtyFlag = true;
32006                                         r.store.remove(r);
32007                                         this.store.insert(insertAt++, r);
32008                                 }
32009                                 this.isDirtyFlag = true;
32010                         }
32011                 }
32012                 this.dragZone.cachedTarget = null;
32013                 return true;
32014     },
32015
32016     removeDropIndicators : function(n){
32017                 if(n){
32018                         Roo.fly(n).removeClass([
32019                                 "x-view-drag-insert-above",
32020                                 "x-view-drag-insert-below"]);
32021                         this.lastInsertClass = "_noclass";
32022                 }
32023     },
32024
32025 /**
32026  *      Utility method. Add a delete option to the DDView's context menu.
32027  *      @param {String} imageUrl The URL of the "delete" icon image.
32028  */
32029         setDeletable: function(imageUrl) {
32030                 if (!this.singleSelect && !this.multiSelect) {
32031                         this.singleSelect = true;
32032                 }
32033                 var c = this.getContextMenu();
32034                 this.contextMenu.on("itemclick", function(item) {
32035                         switch (item.id) {
32036                                 case "delete":
32037                                         this.remove(this.getSelectedIndexes());
32038                                         break;
32039                         }
32040                 }, this);
32041                 this.contextMenu.add({
32042                         icon: imageUrl,
32043                         id: "delete",
32044                         text: 'Delete'
32045                 });
32046         },
32047         
32048 /**     Return the context menu for this DDView. */
32049         getContextMenu: function() {
32050                 if (!this.contextMenu) {
32051 //                      Create the View's context menu
32052                         this.contextMenu = new Roo.menu.Menu({
32053                                 id: this.id + "-contextmenu"
32054                         });
32055                         this.el.on("contextmenu", this.showContextMenu, this);
32056                 }
32057                 return this.contextMenu;
32058         },
32059         
32060         disableContextMenu: function() {
32061                 if (this.contextMenu) {
32062                         this.el.un("contextmenu", this.showContextMenu, this);
32063                 }
32064         },
32065
32066         showContextMenu: function(e, item) {
32067         item = this.findItemFromChild(e.getTarget());
32068                 if (item) {
32069                         e.stopEvent();
32070                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32071                         this.contextMenu.showAt(e.getXY());
32072             }
32073     },
32074
32075 /**
32076  *      Remove {@link Roo.data.Record}s at the specified indices.
32077  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32078  */
32079     remove: function(selectedIndices) {
32080                 selectedIndices = [].concat(selectedIndices);
32081                 for (var i = 0; i < selectedIndices.length; i++) {
32082                         var rec = this.store.getAt(selectedIndices[i]);
32083                         this.store.remove(rec);
32084                 }
32085     },
32086
32087 /**
32088  *      Double click fires the event, but also, if this is draggable, and there is only one other
32089  *      related DropZone, it transfers the selected node.
32090  */
32091     onDblClick : function(e){
32092         var item = this.findItemFromChild(e.getTarget());
32093         if(item){
32094             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32095                 return false;
32096             }
32097             if (this.dragGroup) {
32098                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32099                     while (targets.indexOf(this.dropZone) > -1) {
32100                             targets.remove(this.dropZone);
32101                                 }
32102                     if (targets.length == 1) {
32103                                         this.dragZone.cachedTarget = null;
32104                         var el = Roo.get(targets[0].getEl());
32105                         var box = el.getBox(true);
32106                         targets[0].onNodeDrop(el.dom, {
32107                                 target: el.dom,
32108                                 xy: [box.x, box.y + box.height - 1]
32109                         }, null, this.getDragData(e));
32110                     }
32111                 }
32112         }
32113     },
32114     
32115     handleSelection: function(e) {
32116                 this.dragZone.cachedTarget = null;
32117         var item = this.findItemFromChild(e.getTarget());
32118         if (!item) {
32119                 this.clearSelections(true);
32120                 return;
32121         }
32122                 if (item && (this.multiSelect || this.singleSelect)){
32123                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32124                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32125                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32126                                 this.unselect(item);
32127                         } else {
32128                                 this.select(item, this.multiSelect && e.ctrlKey);
32129                                 this.lastSelection = item;
32130                         }
32131                 }
32132     },
32133
32134     onItemClick : function(item, index, e){
32135                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32136                         return false;
32137                 }
32138                 return true;
32139     },
32140
32141     unselect : function(nodeInfo, suppressEvent){
32142                 var node = this.getNode(nodeInfo);
32143                 if(node && this.isSelected(node)){
32144                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32145                                 Roo.fly(node).removeClass(this.selectedClass);
32146                                 this.selections.remove(node);
32147                                 if(!suppressEvent){
32148                                         this.fireEvent("selectionchange", this, this.selections);
32149                                 }
32150                         }
32151                 }
32152     }
32153 });
32154 /*
32155  * Based on:
32156  * Ext JS Library 1.1.1
32157  * Copyright(c) 2006-2007, Ext JS, LLC.
32158  *
32159  * Originally Released Under LGPL - original licence link has changed is not relivant.
32160  *
32161  * Fork - LGPL
32162  * <script type="text/javascript">
32163  */
32164  
32165 /**
32166  * @class Roo.LayoutManager
32167  * @extends Roo.util.Observable
32168  * Base class for layout managers.
32169  */
32170 Roo.LayoutManager = function(container, config){
32171     Roo.LayoutManager.superclass.constructor.call(this);
32172     this.el = Roo.get(container);
32173     // ie scrollbar fix
32174     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32175         document.body.scroll = "no";
32176     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32177         this.el.position('relative');
32178     }
32179     this.id = this.el.id;
32180     this.el.addClass("x-layout-container");
32181     /** false to disable window resize monitoring @type Boolean */
32182     this.monitorWindowResize = true;
32183     this.regions = {};
32184     this.addEvents({
32185         /**
32186          * @event layout
32187          * Fires when a layout is performed. 
32188          * @param {Roo.LayoutManager} this
32189          */
32190         "layout" : true,
32191         /**
32192          * @event regionresized
32193          * Fires when the user resizes a region. 
32194          * @param {Roo.LayoutRegion} region The resized region
32195          * @param {Number} newSize The new size (width for east/west, height for north/south)
32196          */
32197         "regionresized" : true,
32198         /**
32199          * @event regioncollapsed
32200          * Fires when a region is collapsed. 
32201          * @param {Roo.LayoutRegion} region The collapsed region
32202          */
32203         "regioncollapsed" : true,
32204         /**
32205          * @event regionexpanded
32206          * Fires when a region is expanded.  
32207          * @param {Roo.LayoutRegion} region The expanded region
32208          */
32209         "regionexpanded" : true
32210     });
32211     this.updating = false;
32212     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32213 };
32214
32215 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32216     /**
32217      * Returns true if this layout is currently being updated
32218      * @return {Boolean}
32219      */
32220     isUpdating : function(){
32221         return this.updating; 
32222     },
32223     
32224     /**
32225      * Suspend the LayoutManager from doing auto-layouts while
32226      * making multiple add or remove calls
32227      */
32228     beginUpdate : function(){
32229         this.updating = true;    
32230     },
32231     
32232     /**
32233      * Restore auto-layouts and optionally disable the manager from performing a layout
32234      * @param {Boolean} noLayout true to disable a layout update 
32235      */
32236     endUpdate : function(noLayout){
32237         this.updating = false;
32238         if(!noLayout){
32239             this.layout();
32240         }    
32241     },
32242     
32243     layout: function(){
32244         
32245     },
32246     
32247     onRegionResized : function(region, newSize){
32248         this.fireEvent("regionresized", region, newSize);
32249         this.layout();
32250     },
32251     
32252     onRegionCollapsed : function(region){
32253         this.fireEvent("regioncollapsed", region);
32254     },
32255     
32256     onRegionExpanded : function(region){
32257         this.fireEvent("regionexpanded", region);
32258     },
32259         
32260     /**
32261      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32262      * performs box-model adjustments.
32263      * @return {Object} The size as an object {width: (the width), height: (the height)}
32264      */
32265     getViewSize : function(){
32266         var size;
32267         if(this.el.dom != document.body){
32268             size = this.el.getSize();
32269         }else{
32270             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32271         }
32272         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32273         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32274         return size;
32275     },
32276     
32277     /**
32278      * Returns the Element this layout is bound to.
32279      * @return {Roo.Element}
32280      */
32281     getEl : function(){
32282         return this.el;
32283     },
32284     
32285     /**
32286      * Returns the specified region.
32287      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32288      * @return {Roo.LayoutRegion}
32289      */
32290     getRegion : function(target){
32291         return this.regions[target.toLowerCase()];
32292     },
32293     
32294     onWindowResize : function(){
32295         if(this.monitorWindowResize){
32296             this.layout();
32297         }
32298     }
32299 });/*
32300  * Based on:
32301  * Ext JS Library 1.1.1
32302  * Copyright(c) 2006-2007, Ext JS, LLC.
32303  *
32304  * Originally Released Under LGPL - original licence link has changed is not relivant.
32305  *
32306  * Fork - LGPL
32307  * <script type="text/javascript">
32308  */
32309 /**
32310  * @class Roo.BorderLayout
32311  * @extends Roo.LayoutManager
32312  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32313  * please see: <br><br>
32314  * <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>
32315  * <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>
32316  * Example:
32317  <pre><code>
32318  var layout = new Roo.BorderLayout(document.body, {
32319     north: {
32320         initialSize: 25,
32321         titlebar: false
32322     },
32323     west: {
32324         split:true,
32325         initialSize: 200,
32326         minSize: 175,
32327         maxSize: 400,
32328         titlebar: true,
32329         collapsible: true
32330     },
32331     east: {
32332         split:true,
32333         initialSize: 202,
32334         minSize: 175,
32335         maxSize: 400,
32336         titlebar: true,
32337         collapsible: true
32338     },
32339     south: {
32340         split:true,
32341         initialSize: 100,
32342         minSize: 100,
32343         maxSize: 200,
32344         titlebar: true,
32345         collapsible: true
32346     },
32347     center: {
32348         titlebar: true,
32349         autoScroll:true,
32350         resizeTabs: true,
32351         minTabWidth: 50,
32352         preferredTabWidth: 150
32353     }
32354 });
32355
32356 // shorthand
32357 var CP = Roo.ContentPanel;
32358
32359 layout.beginUpdate();
32360 layout.add("north", new CP("north", "North"));
32361 layout.add("south", new CP("south", {title: "South", closable: true}));
32362 layout.add("west", new CP("west", {title: "West"}));
32363 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32364 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32365 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32366 layout.getRegion("center").showPanel("center1");
32367 layout.endUpdate();
32368 </code></pre>
32369
32370 <b>The container the layout is rendered into can be either the body element or any other element.
32371 If it is not the body element, the container needs to either be an absolute positioned element,
32372 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32373 the container size if it is not the body element.</b>
32374
32375 * @constructor
32376 * Create a new BorderLayout
32377 * @param {String/HTMLElement/Element} container The container this layout is bound to
32378 * @param {Object} config Configuration options
32379  */
32380 Roo.BorderLayout = function(container, config){
32381     config = config || {};
32382     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32383     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32384     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32385         var target = this.factory.validRegions[i];
32386         if(config[target]){
32387             this.addRegion(target, config[target]);
32388         }
32389     }
32390 };
32391
32392 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32393     /**
32394      * Creates and adds a new region if it doesn't already exist.
32395      * @param {String} target The target region key (north, south, east, west or center).
32396      * @param {Object} config The regions config object
32397      * @return {BorderLayoutRegion} The new region
32398      */
32399     addRegion : function(target, config){
32400         if(!this.regions[target]){
32401             var r = this.factory.create(target, this, config);
32402             this.bindRegion(target, r);
32403         }
32404         return this.regions[target];
32405     },
32406
32407     // private (kinda)
32408     bindRegion : function(name, r){
32409         this.regions[name] = r;
32410         r.on("visibilitychange", this.layout, this);
32411         r.on("paneladded", this.layout, this);
32412         r.on("panelremoved", this.layout, this);
32413         r.on("invalidated", this.layout, this);
32414         r.on("resized", this.onRegionResized, this);
32415         r.on("collapsed", this.onRegionCollapsed, this);
32416         r.on("expanded", this.onRegionExpanded, this);
32417     },
32418
32419     /**
32420      * Performs a layout update.
32421      */
32422     layout : function(){
32423         if(this.updating) return;
32424         var size = this.getViewSize();
32425         var w = size.width;
32426         var h = size.height;
32427         var centerW = w;
32428         var centerH = h;
32429         var centerY = 0;
32430         var centerX = 0;
32431         //var x = 0, y = 0;
32432
32433         var rs = this.regions;
32434         var north = rs["north"];
32435         var south = rs["south"]; 
32436         var west = rs["west"];
32437         var east = rs["east"];
32438         var center = rs["center"];
32439         //if(this.hideOnLayout){ // not supported anymore
32440             //c.el.setStyle("display", "none");
32441         //}
32442         if(north && north.isVisible()){
32443             var b = north.getBox();
32444             var m = north.getMargins();
32445             b.width = w - (m.left+m.right);
32446             b.x = m.left;
32447             b.y = m.top;
32448             centerY = b.height + b.y + m.bottom;
32449             centerH -= centerY;
32450             north.updateBox(this.safeBox(b));
32451         }
32452         if(south && south.isVisible()){
32453             var b = south.getBox();
32454             var m = south.getMargins();
32455             b.width = w - (m.left+m.right);
32456             b.x = m.left;
32457             var totalHeight = (b.height + m.top + m.bottom);
32458             b.y = h - totalHeight + m.top;
32459             centerH -= totalHeight;
32460             south.updateBox(this.safeBox(b));
32461         }
32462         if(west && west.isVisible()){
32463             var b = west.getBox();
32464             var m = west.getMargins();
32465             b.height = centerH - (m.top+m.bottom);
32466             b.x = m.left;
32467             b.y = centerY + m.top;
32468             var totalWidth = (b.width + m.left + m.right);
32469             centerX += totalWidth;
32470             centerW -= totalWidth;
32471             west.updateBox(this.safeBox(b));
32472         }
32473         if(east && east.isVisible()){
32474             var b = east.getBox();
32475             var m = east.getMargins();
32476             b.height = centerH - (m.top+m.bottom);
32477             var totalWidth = (b.width + m.left + m.right);
32478             b.x = w - totalWidth + m.left;
32479             b.y = centerY + m.top;
32480             centerW -= totalWidth;
32481             east.updateBox(this.safeBox(b));
32482         }
32483         if(center){
32484             var m = center.getMargins();
32485             var centerBox = {
32486                 x: centerX + m.left,
32487                 y: centerY + m.top,
32488                 width: centerW - (m.left+m.right),
32489                 height: centerH - (m.top+m.bottom)
32490             };
32491             //if(this.hideOnLayout){
32492                 //center.el.setStyle("display", "block");
32493             //}
32494             center.updateBox(this.safeBox(centerBox));
32495         }
32496         this.el.repaint();
32497         this.fireEvent("layout", this);
32498     },
32499
32500     // private
32501     safeBox : function(box){
32502         box.width = Math.max(0, box.width);
32503         box.height = Math.max(0, box.height);
32504         return box;
32505     },
32506
32507     /**
32508      * Adds a ContentPanel (or subclass) to this layout.
32509      * @param {String} target The target region key (north, south, east, west or center).
32510      * @param {Roo.ContentPanel} panel The panel to add
32511      * @return {Roo.ContentPanel} The added panel
32512      */
32513     add : function(target, panel){
32514          
32515         target = target.toLowerCase();
32516         return this.regions[target].add(panel);
32517     },
32518
32519     /**
32520      * Remove a ContentPanel (or subclass) to this layout.
32521      * @param {String} target The target region key (north, south, east, west or center).
32522      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32523      * @return {Roo.ContentPanel} The removed panel
32524      */
32525     remove : function(target, panel){
32526         target = target.toLowerCase();
32527         return this.regions[target].remove(panel);
32528     },
32529
32530     /**
32531      * Searches all regions for a panel with the specified id
32532      * @param {String} panelId
32533      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32534      */
32535     findPanel : function(panelId){
32536         var rs = this.regions;
32537         for(var target in rs){
32538             if(typeof rs[target] != "function"){
32539                 var p = rs[target].getPanel(panelId);
32540                 if(p){
32541                     return p;
32542                 }
32543             }
32544         }
32545         return null;
32546     },
32547
32548     /**
32549      * Searches all regions for a panel with the specified id and activates (shows) it.
32550      * @param {String/ContentPanel} panelId The panels id or the panel itself
32551      * @return {Roo.ContentPanel} The shown panel or null
32552      */
32553     showPanel : function(panelId) {
32554       var rs = this.regions;
32555       for(var target in rs){
32556          var r = rs[target];
32557          if(typeof r != "function"){
32558             if(r.hasPanel(panelId)){
32559                return r.showPanel(panelId);
32560             }
32561          }
32562       }
32563       return null;
32564    },
32565
32566    /**
32567      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32568      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32569      */
32570     restoreState : function(provider){
32571         if(!provider){
32572             provider = Roo.state.Manager;
32573         }
32574         var sm = new Roo.LayoutStateManager();
32575         sm.init(this, provider);
32576     },
32577
32578     /**
32579      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
32580      * object should contain properties for each region to add ContentPanels to, and each property's value should be
32581      * a valid ContentPanel config object.  Example:
32582      * <pre><code>
32583 // Create the main layout
32584 var layout = new Roo.BorderLayout('main-ct', {
32585     west: {
32586         split:true,
32587         minSize: 175,
32588         titlebar: true
32589     },
32590     center: {
32591         title:'Components'
32592     }
32593 }, 'main-ct');
32594
32595 // Create and add multiple ContentPanels at once via configs
32596 layout.batchAdd({
32597    west: {
32598        id: 'source-files',
32599        autoCreate:true,
32600        title:'Ext Source Files',
32601        autoScroll:true,
32602        fitToFrame:true
32603    },
32604    center : {
32605        el: cview,
32606        autoScroll:true,
32607        fitToFrame:true,
32608        toolbar: tb,
32609        resizeEl:'cbody'
32610    }
32611 });
32612 </code></pre>
32613      * @param {Object} regions An object containing ContentPanel configs by region name
32614      */
32615     batchAdd : function(regions){
32616         this.beginUpdate();
32617         for(var rname in regions){
32618             var lr = this.regions[rname];
32619             if(lr){
32620                 this.addTypedPanels(lr, regions[rname]);
32621             }
32622         }
32623         this.endUpdate();
32624     },
32625
32626     // private
32627     addTypedPanels : function(lr, ps){
32628         if(typeof ps == 'string'){
32629             lr.add(new Roo.ContentPanel(ps));
32630         }
32631         else if(ps instanceof Array){
32632             for(var i =0, len = ps.length; i < len; i++){
32633                 this.addTypedPanels(lr, ps[i]);
32634             }
32635         }
32636         else if(!ps.events){ // raw config?
32637             var el = ps.el;
32638             delete ps.el; // prevent conflict
32639             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32640         }
32641         else {  // panel object assumed!
32642             lr.add(ps);
32643         }
32644     },
32645     /**
32646      * Adds a xtype elements to the layout.
32647      * <pre><code>
32648
32649 layout.addxtype({
32650        xtype : 'ContentPanel',
32651        region: 'west',
32652        items: [ .... ]
32653    }
32654 );
32655
32656 layout.addxtype({
32657         xtype : 'NestedLayoutPanel',
32658         region: 'west',
32659         layout: {
32660            center: { },
32661            west: { }   
32662         },
32663         items : [ ... list of content panels or nested layout panels.. ]
32664    }
32665 );
32666 </code></pre>
32667      * @param {Object} cfg Xtype definition of item to add.
32668      */
32669     addxtype : function(cfg)
32670     {
32671         // basically accepts a pannel...
32672         // can accept a layout region..!?!?
32673         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32674         
32675         if (!cfg.xtype.match(/Panel$/)) {
32676             return false;
32677         }
32678         var ret = false;
32679         
32680         if (typeof(cfg.region) == 'undefined') {
32681             Roo.log("Failed to add Panel, region was not set");
32682             Roo.log(cfg);
32683             return false;
32684         }
32685         var region = cfg.region;
32686         delete cfg.region;
32687         
32688           
32689         var xitems = [];
32690         if (cfg.items) {
32691             xitems = cfg.items;
32692             delete cfg.items;
32693         }
32694         var nb = false;
32695         
32696         switch(cfg.xtype) 
32697         {
32698             case 'ContentPanel':  // ContentPanel (el, cfg)
32699             case 'ScrollPanel':  // ContentPanel (el, cfg)
32700             case 'ViewPanel': 
32701                 if(cfg.autoCreate) {
32702                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32703                 } else {
32704                     var el = this.el.createChild();
32705                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32706                 }
32707                 
32708                 this.add(region, ret);
32709                 break;
32710             
32711             
32712             case 'TreePanel': // our new panel!
32713                 cfg.el = this.el.createChild();
32714                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32715                 this.add(region, ret);
32716                 break;
32717             
32718             case 'NestedLayoutPanel': 
32719                 // create a new Layout (which is  a Border Layout...
32720                 var el = this.el.createChild();
32721                 var clayout = cfg.layout;
32722                 delete cfg.layout;
32723                 clayout.items   = clayout.items  || [];
32724                 // replace this exitems with the clayout ones..
32725                 xitems = clayout.items;
32726                  
32727                 
32728                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32729                     cfg.background = false;
32730                 }
32731                 var layout = new Roo.BorderLayout(el, clayout);
32732                 
32733                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32734                 //console.log('adding nested layout panel '  + cfg.toSource());
32735                 this.add(region, ret);
32736                 nb = {}; /// find first...
32737                 break;
32738                 
32739             case 'GridPanel': 
32740             
32741                 // needs grid and region
32742                 
32743                 //var el = this.getRegion(region).el.createChild();
32744                 var el = this.el.createChild();
32745                 // create the grid first...
32746                 
32747                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32748                 delete cfg.grid;
32749                 if (region == 'center' && this.active ) {
32750                     cfg.background = false;
32751                 }
32752                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32753                 
32754                 this.add(region, ret);
32755                 if (cfg.background) {
32756                     ret.on('activate', function(gp) {
32757                         if (!gp.grid.rendered) {
32758                             gp.grid.render();
32759                         }
32760                     });
32761                 } else {
32762                     grid.render();
32763                 }
32764                 break;
32765            
32766            
32767            
32768                 
32769                 
32770                 
32771             default: 
32772                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32773                 return null;
32774              // GridPanel (grid, cfg)
32775             
32776         }
32777         this.beginUpdate();
32778         // add children..
32779         var region = '';
32780         var abn = {};
32781         Roo.each(xitems, function(i)  {
32782             region = nb && i.region ? i.region : false;
32783             
32784             var add = ret.addxtype(i);
32785            
32786             if (region) {
32787                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32788                 if (!i.background) {
32789                     abn[region] = nb[region] ;
32790                 }
32791             }
32792             
32793         });
32794         this.endUpdate();
32795
32796         // make the last non-background panel active..
32797         //if (nb) { Roo.log(abn); }
32798         if (nb) {
32799             
32800             for(var r in abn) {
32801                 region = this.getRegion(r);
32802                 if (region) {
32803                     // tried using nb[r], but it does not work..
32804                      
32805                     region.showPanel(abn[r]);
32806                    
32807                 }
32808             }
32809         }
32810         return ret;
32811         
32812     }
32813 });
32814
32815 /**
32816  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32817  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
32818  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32819  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
32820  * <pre><code>
32821 // shorthand
32822 var CP = Roo.ContentPanel;
32823
32824 var layout = Roo.BorderLayout.create({
32825     north: {
32826         initialSize: 25,
32827         titlebar: false,
32828         panels: [new CP("north", "North")]
32829     },
32830     west: {
32831         split:true,
32832         initialSize: 200,
32833         minSize: 175,
32834         maxSize: 400,
32835         titlebar: true,
32836         collapsible: true,
32837         panels: [new CP("west", {title: "West"})]
32838     },
32839     east: {
32840         split:true,
32841         initialSize: 202,
32842         minSize: 175,
32843         maxSize: 400,
32844         titlebar: true,
32845         collapsible: true,
32846         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
32847     },
32848     south: {
32849         split:true,
32850         initialSize: 100,
32851         minSize: 100,
32852         maxSize: 200,
32853         titlebar: true,
32854         collapsible: true,
32855         panels: [new CP("south", {title: "South", closable: true})]
32856     },
32857     center: {
32858         titlebar: true,
32859         autoScroll:true,
32860         resizeTabs: true,
32861         minTabWidth: 50,
32862         preferredTabWidth: 150,
32863         panels: [
32864             new CP("center1", {title: "Close Me", closable: true}),
32865             new CP("center2", {title: "Center Panel", closable: false})
32866         ]
32867     }
32868 }, document.body);
32869
32870 layout.getRegion("center").showPanel("center1");
32871 </code></pre>
32872  * @param config
32873  * @param targetEl
32874  */
32875 Roo.BorderLayout.create = function(config, targetEl){
32876     var layout = new Roo.BorderLayout(targetEl || document.body, config);
32877     layout.beginUpdate();
32878     var regions = Roo.BorderLayout.RegionFactory.validRegions;
32879     for(var j = 0, jlen = regions.length; j < jlen; j++){
32880         var lr = regions[j];
32881         if(layout.regions[lr] && config[lr].panels){
32882             var r = layout.regions[lr];
32883             var ps = config[lr].panels;
32884             layout.addTypedPanels(r, ps);
32885         }
32886     }
32887     layout.endUpdate();
32888     return layout;
32889 };
32890
32891 // private
32892 Roo.BorderLayout.RegionFactory = {
32893     // private
32894     validRegions : ["north","south","east","west","center"],
32895
32896     // private
32897     create : function(target, mgr, config){
32898         target = target.toLowerCase();
32899         if(config.lightweight || config.basic){
32900             return new Roo.BasicLayoutRegion(mgr, config, target);
32901         }
32902         switch(target){
32903             case "north":
32904                 return new Roo.NorthLayoutRegion(mgr, config);
32905             case "south":
32906                 return new Roo.SouthLayoutRegion(mgr, config);
32907             case "east":
32908                 return new Roo.EastLayoutRegion(mgr, config);
32909             case "west":
32910                 return new Roo.WestLayoutRegion(mgr, config);
32911             case "center":
32912                 return new Roo.CenterLayoutRegion(mgr, config);
32913         }
32914         throw 'Layout region "'+target+'" not supported.';
32915     }
32916 };/*
32917  * Based on:
32918  * Ext JS Library 1.1.1
32919  * Copyright(c) 2006-2007, Ext JS, LLC.
32920  *
32921  * Originally Released Under LGPL - original licence link has changed is not relivant.
32922  *
32923  * Fork - LGPL
32924  * <script type="text/javascript">
32925  */
32926  
32927 /**
32928  * @class Roo.BasicLayoutRegion
32929  * @extends Roo.util.Observable
32930  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32931  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32932  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32933  */
32934 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
32935     this.mgr = mgr;
32936     this.position  = pos;
32937     this.events = {
32938         /**
32939          * @scope Roo.BasicLayoutRegion
32940          */
32941         
32942         /**
32943          * @event beforeremove
32944          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32945          * @param {Roo.LayoutRegion} this
32946          * @param {Roo.ContentPanel} panel The panel
32947          * @param {Object} e The cancel event object
32948          */
32949         "beforeremove" : true,
32950         /**
32951          * @event invalidated
32952          * Fires when the layout for this region is changed.
32953          * @param {Roo.LayoutRegion} this
32954          */
32955         "invalidated" : true,
32956         /**
32957          * @event visibilitychange
32958          * Fires when this region is shown or hidden 
32959          * @param {Roo.LayoutRegion} this
32960          * @param {Boolean} visibility true or false
32961          */
32962         "visibilitychange" : true,
32963         /**
32964          * @event paneladded
32965          * Fires when a panel is added. 
32966          * @param {Roo.LayoutRegion} this
32967          * @param {Roo.ContentPanel} panel The panel
32968          */
32969         "paneladded" : true,
32970         /**
32971          * @event panelremoved
32972          * Fires when a panel is removed. 
32973          * @param {Roo.LayoutRegion} this
32974          * @param {Roo.ContentPanel} panel The panel
32975          */
32976         "panelremoved" : true,
32977         /**
32978          * @event collapsed
32979          * Fires when this region is collapsed.
32980          * @param {Roo.LayoutRegion} this
32981          */
32982         "collapsed" : true,
32983         /**
32984          * @event expanded
32985          * Fires when this region is expanded.
32986          * @param {Roo.LayoutRegion} this
32987          */
32988         "expanded" : true,
32989         /**
32990          * @event slideshow
32991          * Fires when this region is slid into view.
32992          * @param {Roo.LayoutRegion} this
32993          */
32994         "slideshow" : true,
32995         /**
32996          * @event slidehide
32997          * Fires when this region slides out of view. 
32998          * @param {Roo.LayoutRegion} this
32999          */
33000         "slidehide" : true,
33001         /**
33002          * @event panelactivated
33003          * Fires when a panel is activated. 
33004          * @param {Roo.LayoutRegion} this
33005          * @param {Roo.ContentPanel} panel The activated panel
33006          */
33007         "panelactivated" : true,
33008         /**
33009          * @event resized
33010          * Fires when the user resizes this region. 
33011          * @param {Roo.LayoutRegion} this
33012          * @param {Number} newSize The new size (width for east/west, height for north/south)
33013          */
33014         "resized" : true
33015     };
33016     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33017     this.panels = new Roo.util.MixedCollection();
33018     this.panels.getKey = this.getPanelId.createDelegate(this);
33019     this.box = null;
33020     this.activePanel = null;
33021     // ensure listeners are added...
33022     
33023     if (config.listeners || config.events) {
33024         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33025             listeners : config.listeners || {},
33026             events : config.events || {}
33027         });
33028     }
33029     
33030     if(skipConfig !== true){
33031         this.applyConfig(config);
33032     }
33033 };
33034
33035 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33036     getPanelId : function(p){
33037         return p.getId();
33038     },
33039     
33040     applyConfig : function(config){
33041         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33042         this.config = config;
33043         
33044     },
33045     
33046     /**
33047      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33048      * the width, for horizontal (north, south) the height.
33049      * @param {Number} newSize The new width or height
33050      */
33051     resizeTo : function(newSize){
33052         var el = this.el ? this.el :
33053                  (this.activePanel ? this.activePanel.getEl() : null);
33054         if(el){
33055             switch(this.position){
33056                 case "east":
33057                 case "west":
33058                     el.setWidth(newSize);
33059                     this.fireEvent("resized", this, newSize);
33060                 break;
33061                 case "north":
33062                 case "south":
33063                     el.setHeight(newSize);
33064                     this.fireEvent("resized", this, newSize);
33065                 break;                
33066             }
33067         }
33068     },
33069     
33070     getBox : function(){
33071         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33072     },
33073     
33074     getMargins : function(){
33075         return this.margins;
33076     },
33077     
33078     updateBox : function(box){
33079         this.box = box;
33080         var el = this.activePanel.getEl();
33081         el.dom.style.left = box.x + "px";
33082         el.dom.style.top = box.y + "px";
33083         this.activePanel.setSize(box.width, box.height);
33084     },
33085     
33086     /**
33087      * Returns the container element for this region.
33088      * @return {Roo.Element}
33089      */
33090     getEl : function(){
33091         return this.activePanel;
33092     },
33093     
33094     /**
33095      * Returns true if this region is currently visible.
33096      * @return {Boolean}
33097      */
33098     isVisible : function(){
33099         return this.activePanel ? true : false;
33100     },
33101     
33102     setActivePanel : function(panel){
33103         panel = this.getPanel(panel);
33104         if(this.activePanel && this.activePanel != panel){
33105             this.activePanel.setActiveState(false);
33106             this.activePanel.getEl().setLeftTop(-10000,-10000);
33107         }
33108         this.activePanel = panel;
33109         panel.setActiveState(true);
33110         if(this.box){
33111             panel.setSize(this.box.width, this.box.height);
33112         }
33113         this.fireEvent("panelactivated", this, panel);
33114         this.fireEvent("invalidated");
33115     },
33116     
33117     /**
33118      * Show the specified panel.
33119      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33120      * @return {Roo.ContentPanel} The shown panel or null
33121      */
33122     showPanel : function(panel){
33123         if(panel = this.getPanel(panel)){
33124             this.setActivePanel(panel);
33125         }
33126         return panel;
33127     },
33128     
33129     /**
33130      * Get the active panel for this region.
33131      * @return {Roo.ContentPanel} The active panel or null
33132      */
33133     getActivePanel : function(){
33134         return this.activePanel;
33135     },
33136     
33137     /**
33138      * Add the passed ContentPanel(s)
33139      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33140      * @return {Roo.ContentPanel} The panel added (if only one was added)
33141      */
33142     add : function(panel){
33143         if(arguments.length > 1){
33144             for(var i = 0, len = arguments.length; i < len; i++) {
33145                 this.add(arguments[i]);
33146             }
33147             return null;
33148         }
33149         if(this.hasPanel(panel)){
33150             this.showPanel(panel);
33151             return panel;
33152         }
33153         var el = panel.getEl();
33154         if(el.dom.parentNode != this.mgr.el.dom){
33155             this.mgr.el.dom.appendChild(el.dom);
33156         }
33157         if(panel.setRegion){
33158             panel.setRegion(this);
33159         }
33160         this.panels.add(panel);
33161         el.setStyle("position", "absolute");
33162         if(!panel.background){
33163             this.setActivePanel(panel);
33164             if(this.config.initialSize && this.panels.getCount()==1){
33165                 this.resizeTo(this.config.initialSize);
33166             }
33167         }
33168         this.fireEvent("paneladded", this, panel);
33169         return panel;
33170     },
33171     
33172     /**
33173      * Returns true if the panel is in this region.
33174      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33175      * @return {Boolean}
33176      */
33177     hasPanel : function(panel){
33178         if(typeof panel == "object"){ // must be panel obj
33179             panel = panel.getId();
33180         }
33181         return this.getPanel(panel) ? true : false;
33182     },
33183     
33184     /**
33185      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33186      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33187      * @param {Boolean} preservePanel Overrides the config preservePanel option
33188      * @return {Roo.ContentPanel} The panel that was removed
33189      */
33190     remove : function(panel, preservePanel){
33191         panel = this.getPanel(panel);
33192         if(!panel){
33193             return null;
33194         }
33195         var e = {};
33196         this.fireEvent("beforeremove", this, panel, e);
33197         if(e.cancel === true){
33198             return null;
33199         }
33200         var panelId = panel.getId();
33201         this.panels.removeKey(panelId);
33202         return panel;
33203     },
33204     
33205     /**
33206      * Returns the panel specified or null if it's not in this region.
33207      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33208      * @return {Roo.ContentPanel}
33209      */
33210     getPanel : function(id){
33211         if(typeof id == "object"){ // must be panel obj
33212             return id;
33213         }
33214         return this.panels.get(id);
33215     },
33216     
33217     /**
33218      * Returns this regions position (north/south/east/west/center).
33219      * @return {String} 
33220      */
33221     getPosition: function(){
33222         return this.position;    
33223     }
33224 });/*
33225  * Based on:
33226  * Ext JS Library 1.1.1
33227  * Copyright(c) 2006-2007, Ext JS, LLC.
33228  *
33229  * Originally Released Under LGPL - original licence link has changed is not relivant.
33230  *
33231  * Fork - LGPL
33232  * <script type="text/javascript">
33233  */
33234  
33235 /**
33236  * @class Roo.LayoutRegion
33237  * @extends Roo.BasicLayoutRegion
33238  * This class represents a region in a layout manager.
33239  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33240  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33241  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33242  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33243  * @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})
33244  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
33245  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33246  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33247  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33248  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33249  * @cfg {String}    title           The title for the region (overrides panel titles)
33250  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33251  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33252  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33253  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33254  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33255  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33256  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33257  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33258  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33259  * @cfg {Boolean}   showPin         True to show a pin button
33260  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33261  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33262  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33263  * @cfg {Number}    width           For East/West panels
33264  * @cfg {Number}    height          For North/South panels
33265  * @cfg {Boolean}   split           To show the splitter
33266  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33267  */
33268 Roo.LayoutRegion = function(mgr, config, pos){
33269     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33270     var dh = Roo.DomHelper;
33271     /** This region's container element 
33272     * @type Roo.Element */
33273     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33274     /** This region's title element 
33275     * @type Roo.Element */
33276
33277     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33278         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33279         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33280     ]}, true);
33281     this.titleEl.enableDisplayMode();
33282     /** This region's title text element 
33283     * @type HTMLElement */
33284     this.titleTextEl = this.titleEl.dom.firstChild;
33285     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33286     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33287     this.closeBtn.enableDisplayMode();
33288     this.closeBtn.on("click", this.closeClicked, this);
33289     this.closeBtn.hide();
33290
33291     this.createBody(config);
33292     this.visible = true;
33293     this.collapsed = false;
33294
33295     if(config.hideWhenEmpty){
33296         this.hide();
33297         this.on("paneladded", this.validateVisibility, this);
33298         this.on("panelremoved", this.validateVisibility, this);
33299     }
33300     this.applyConfig(config);
33301 };
33302
33303 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33304
33305     createBody : function(){
33306         /** This region's body element 
33307         * @type Roo.Element */
33308         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33309     },
33310
33311     applyConfig : function(c){
33312         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33313             var dh = Roo.DomHelper;
33314             if(c.titlebar !== false){
33315                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33316                 this.collapseBtn.on("click", this.collapse, this);
33317                 this.collapseBtn.enableDisplayMode();
33318
33319                 if(c.showPin === true || this.showPin){
33320                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33321                     this.stickBtn.enableDisplayMode();
33322                     this.stickBtn.on("click", this.expand, this);
33323                     this.stickBtn.hide();
33324                 }
33325             }
33326             /** This region's collapsed element
33327             * @type Roo.Element */
33328             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33329                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33330             ]}, true);
33331             if(c.floatable !== false){
33332                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33333                this.collapsedEl.on("click", this.collapseClick, this);
33334             }
33335
33336             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33337                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33338                    id: "message", unselectable: "on", style:{"float":"left"}});
33339                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33340              }
33341             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33342             this.expandBtn.on("click", this.expand, this);
33343         }
33344         if(this.collapseBtn){
33345             this.collapseBtn.setVisible(c.collapsible == true);
33346         }
33347         this.cmargins = c.cmargins || this.cmargins ||
33348                          (this.position == "west" || this.position == "east" ?
33349                              {top: 0, left: 2, right:2, bottom: 0} :
33350                              {top: 2, left: 0, right:0, bottom: 2});
33351         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33352         this.bottomTabs = c.tabPosition != "top";
33353         this.autoScroll = c.autoScroll || false;
33354         if(this.autoScroll){
33355             this.bodyEl.setStyle("overflow", "auto");
33356         }else{
33357             this.bodyEl.setStyle("overflow", "hidden");
33358         }
33359         //if(c.titlebar !== false){
33360             if((!c.titlebar && !c.title) || c.titlebar === false){
33361                 this.titleEl.hide();
33362             }else{
33363                 this.titleEl.show();
33364                 if(c.title){
33365                     this.titleTextEl.innerHTML = c.title;
33366                 }
33367             }
33368         //}
33369         this.duration = c.duration || .30;
33370         this.slideDuration = c.slideDuration || .45;
33371         this.config = c;
33372         if(c.collapsed){
33373             this.collapse(true);
33374         }
33375         if(c.hidden){
33376             this.hide();
33377         }
33378     },
33379     /**
33380      * Returns true if this region is currently visible.
33381      * @return {Boolean}
33382      */
33383     isVisible : function(){
33384         return this.visible;
33385     },
33386
33387     /**
33388      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33389      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33390      */
33391     setCollapsedTitle : function(title){
33392         title = title || "&#160;";
33393         if(this.collapsedTitleTextEl){
33394             this.collapsedTitleTextEl.innerHTML = title;
33395         }
33396     },
33397
33398     getBox : function(){
33399         var b;
33400         if(!this.collapsed){
33401             b = this.el.getBox(false, true);
33402         }else{
33403             b = this.collapsedEl.getBox(false, true);
33404         }
33405         return b;
33406     },
33407
33408     getMargins : function(){
33409         return this.collapsed ? this.cmargins : this.margins;
33410     },
33411
33412     highlight : function(){
33413         this.el.addClass("x-layout-panel-dragover");
33414     },
33415
33416     unhighlight : function(){
33417         this.el.removeClass("x-layout-panel-dragover");
33418     },
33419
33420     updateBox : function(box){
33421         this.box = box;
33422         if(!this.collapsed){
33423             this.el.dom.style.left = box.x + "px";
33424             this.el.dom.style.top = box.y + "px";
33425             this.updateBody(box.width, box.height);
33426         }else{
33427             this.collapsedEl.dom.style.left = box.x + "px";
33428             this.collapsedEl.dom.style.top = box.y + "px";
33429             this.collapsedEl.setSize(box.width, box.height);
33430         }
33431         if(this.tabs){
33432             this.tabs.autoSizeTabs();
33433         }
33434     },
33435
33436     updateBody : function(w, h){
33437         if(w !== null){
33438             this.el.setWidth(w);
33439             w -= this.el.getBorderWidth("rl");
33440             if(this.config.adjustments){
33441                 w += this.config.adjustments[0];
33442             }
33443         }
33444         if(h !== null){
33445             this.el.setHeight(h);
33446             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33447             h -= this.el.getBorderWidth("tb");
33448             if(this.config.adjustments){
33449                 h += this.config.adjustments[1];
33450             }
33451             this.bodyEl.setHeight(h);
33452             if(this.tabs){
33453                 h = this.tabs.syncHeight(h);
33454             }
33455         }
33456         if(this.panelSize){
33457             w = w !== null ? w : this.panelSize.width;
33458             h = h !== null ? h : this.panelSize.height;
33459         }
33460         if(this.activePanel){
33461             var el = this.activePanel.getEl();
33462             w = w !== null ? w : el.getWidth();
33463             h = h !== null ? h : el.getHeight();
33464             this.panelSize = {width: w, height: h};
33465             this.activePanel.setSize(w, h);
33466         }
33467         if(Roo.isIE && this.tabs){
33468             this.tabs.el.repaint();
33469         }
33470     },
33471
33472     /**
33473      * Returns the container element for this region.
33474      * @return {Roo.Element}
33475      */
33476     getEl : function(){
33477         return this.el;
33478     },
33479
33480     /**
33481      * Hides this region.
33482      */
33483     hide : function(){
33484         if(!this.collapsed){
33485             this.el.dom.style.left = "-2000px";
33486             this.el.hide();
33487         }else{
33488             this.collapsedEl.dom.style.left = "-2000px";
33489             this.collapsedEl.hide();
33490         }
33491         this.visible = false;
33492         this.fireEvent("visibilitychange", this, false);
33493     },
33494
33495     /**
33496      * Shows this region if it was previously hidden.
33497      */
33498     show : function(){
33499         if(!this.collapsed){
33500             this.el.show();
33501         }else{
33502             this.collapsedEl.show();
33503         }
33504         this.visible = true;
33505         this.fireEvent("visibilitychange", this, true);
33506     },
33507
33508     closeClicked : function(){
33509         if(this.activePanel){
33510             this.remove(this.activePanel);
33511         }
33512     },
33513
33514     collapseClick : function(e){
33515         if(this.isSlid){
33516            e.stopPropagation();
33517            this.slideIn();
33518         }else{
33519            e.stopPropagation();
33520            this.slideOut();
33521         }
33522     },
33523
33524     /**
33525      * Collapses this region.
33526      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33527      */
33528     collapse : function(skipAnim){
33529         if(this.collapsed) return;
33530         this.collapsed = true;
33531         if(this.split){
33532             this.split.el.hide();
33533         }
33534         if(this.config.animate && skipAnim !== true){
33535             this.fireEvent("invalidated", this);
33536             this.animateCollapse();
33537         }else{
33538             this.el.setLocation(-20000,-20000);
33539             this.el.hide();
33540             this.collapsedEl.show();
33541             this.fireEvent("collapsed", this);
33542             this.fireEvent("invalidated", this);
33543         }
33544     },
33545
33546     animateCollapse : function(){
33547         // overridden
33548     },
33549
33550     /**
33551      * Expands this region if it was previously collapsed.
33552      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33553      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33554      */
33555     expand : function(e, skipAnim){
33556         if(e) e.stopPropagation();
33557         if(!this.collapsed || this.el.hasActiveFx()) return;
33558         if(this.isSlid){
33559             this.afterSlideIn();
33560             skipAnim = true;
33561         }
33562         this.collapsed = false;
33563         if(this.config.animate && skipAnim !== true){
33564             this.animateExpand();
33565         }else{
33566             this.el.show();
33567             if(this.split){
33568                 this.split.el.show();
33569             }
33570             this.collapsedEl.setLocation(-2000,-2000);
33571             this.collapsedEl.hide();
33572             this.fireEvent("invalidated", this);
33573             this.fireEvent("expanded", this);
33574         }
33575     },
33576
33577     animateExpand : function(){
33578         // overridden
33579     },
33580
33581     initTabs : function()
33582     {
33583         this.bodyEl.setStyle("overflow", "hidden");
33584         var ts = new Roo.TabPanel(
33585                 this.bodyEl.dom,
33586                 {
33587                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
33588                     disableTooltips: this.config.disableTabTips,
33589                     toolbar : this.config.toolbar
33590                 }
33591         );
33592         if(this.config.hideTabs){
33593             ts.stripWrap.setDisplayed(false);
33594         }
33595         this.tabs = ts;
33596         ts.resizeTabs = this.config.resizeTabs === true;
33597         ts.minTabWidth = this.config.minTabWidth || 40;
33598         ts.maxTabWidth = this.config.maxTabWidth || 250;
33599         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33600         ts.monitorResize = false;
33601         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33602         ts.bodyEl.addClass('x-layout-tabs-body');
33603         this.panels.each(this.initPanelAsTab, this);
33604     },
33605
33606     initPanelAsTab : function(panel){
33607         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33608                     this.config.closeOnTab && panel.isClosable());
33609         if(panel.tabTip !== undefined){
33610             ti.setTooltip(panel.tabTip);
33611         }
33612         ti.on("activate", function(){
33613               this.setActivePanel(panel);
33614         }, this);
33615         if(this.config.closeOnTab){
33616             ti.on("beforeclose", function(t, e){
33617                 e.cancel = true;
33618                 this.remove(panel);
33619             }, this);
33620         }
33621         return ti;
33622     },
33623
33624     updatePanelTitle : function(panel, title){
33625         if(this.activePanel == panel){
33626             this.updateTitle(title);
33627         }
33628         if(this.tabs){
33629             var ti = this.tabs.getTab(panel.getEl().id);
33630             ti.setText(title);
33631             if(panel.tabTip !== undefined){
33632                 ti.setTooltip(panel.tabTip);
33633             }
33634         }
33635     },
33636
33637     updateTitle : function(title){
33638         if(this.titleTextEl && !this.config.title){
33639             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33640         }
33641     },
33642
33643     setActivePanel : function(panel){
33644         panel = this.getPanel(panel);
33645         if(this.activePanel && this.activePanel != panel){
33646             this.activePanel.setActiveState(false);
33647         }
33648         this.activePanel = panel;
33649         panel.setActiveState(true);
33650         if(this.panelSize){
33651             panel.setSize(this.panelSize.width, this.panelSize.height);
33652         }
33653         if(this.closeBtn){
33654             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33655         }
33656         this.updateTitle(panel.getTitle());
33657         if(this.tabs){
33658             this.fireEvent("invalidated", this);
33659         }
33660         this.fireEvent("panelactivated", this, panel);
33661     },
33662
33663     /**
33664      * Shows the specified panel.
33665      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33666      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33667      */
33668     showPanel : function(panel){
33669         if(panel = this.getPanel(panel)){
33670             if(this.tabs){
33671                 var tab = this.tabs.getTab(panel.getEl().id);
33672                 if(tab.isHidden()){
33673                     this.tabs.unhideTab(tab.id);
33674                 }
33675                 tab.activate();
33676             }else{
33677                 this.setActivePanel(panel);
33678             }
33679         }
33680         return panel;
33681     },
33682
33683     /**
33684      * Get the active panel for this region.
33685      * @return {Roo.ContentPanel} The active panel or null
33686      */
33687     getActivePanel : function(){
33688         return this.activePanel;
33689     },
33690
33691     validateVisibility : function(){
33692         if(this.panels.getCount() < 1){
33693             this.updateTitle("&#160;");
33694             this.closeBtn.hide();
33695             this.hide();
33696         }else{
33697             if(!this.isVisible()){
33698                 this.show();
33699             }
33700         }
33701     },
33702
33703     /**
33704      * Adds the passed ContentPanel(s) to this region.
33705      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33706      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33707      */
33708     add : function(panel){
33709         if(arguments.length > 1){
33710             for(var i = 0, len = arguments.length; i < len; i++) {
33711                 this.add(arguments[i]);
33712             }
33713             return null;
33714         }
33715         if(this.hasPanel(panel)){
33716             this.showPanel(panel);
33717             return panel;
33718         }
33719         panel.setRegion(this);
33720         this.panels.add(panel);
33721         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33722             this.bodyEl.dom.appendChild(panel.getEl().dom);
33723             if(panel.background !== true){
33724                 this.setActivePanel(panel);
33725             }
33726             this.fireEvent("paneladded", this, panel);
33727             return panel;
33728         }
33729         if(!this.tabs){
33730             this.initTabs();
33731         }else{
33732             this.initPanelAsTab(panel);
33733         }
33734         if(panel.background !== true){
33735             this.tabs.activate(panel.getEl().id);
33736         }
33737         this.fireEvent("paneladded", this, panel);
33738         return panel;
33739     },
33740
33741     /**
33742      * Hides the tab for the specified panel.
33743      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33744      */
33745     hidePanel : function(panel){
33746         if(this.tabs && (panel = this.getPanel(panel))){
33747             this.tabs.hideTab(panel.getEl().id);
33748         }
33749     },
33750
33751     /**
33752      * Unhides the tab for a previously hidden panel.
33753      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33754      */
33755     unhidePanel : function(panel){
33756         if(this.tabs && (panel = this.getPanel(panel))){
33757             this.tabs.unhideTab(panel.getEl().id);
33758         }
33759     },
33760
33761     clearPanels : function(){
33762         while(this.panels.getCount() > 0){
33763              this.remove(this.panels.first());
33764         }
33765     },
33766
33767     /**
33768      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33769      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33770      * @param {Boolean} preservePanel Overrides the config preservePanel option
33771      * @return {Roo.ContentPanel} The panel that was removed
33772      */
33773     remove : function(panel, preservePanel){
33774         panel = this.getPanel(panel);
33775         if(!panel){
33776             return null;
33777         }
33778         var e = {};
33779         this.fireEvent("beforeremove", this, panel, e);
33780         if(e.cancel === true){
33781             return null;
33782         }
33783         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33784         var panelId = panel.getId();
33785         this.panels.removeKey(panelId);
33786         if(preservePanel){
33787             document.body.appendChild(panel.getEl().dom);
33788         }
33789         if(this.tabs){
33790             this.tabs.removeTab(panel.getEl().id);
33791         }else if (!preservePanel){
33792             this.bodyEl.dom.removeChild(panel.getEl().dom);
33793         }
33794         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33795             var p = this.panels.first();
33796             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33797             tempEl.appendChild(p.getEl().dom);
33798             this.bodyEl.update("");
33799             this.bodyEl.dom.appendChild(p.getEl().dom);
33800             tempEl = null;
33801             this.updateTitle(p.getTitle());
33802             this.tabs = null;
33803             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33804             this.setActivePanel(p);
33805         }
33806         panel.setRegion(null);
33807         if(this.activePanel == panel){
33808             this.activePanel = null;
33809         }
33810         if(this.config.autoDestroy !== false && preservePanel !== true){
33811             try{panel.destroy();}catch(e){}
33812         }
33813         this.fireEvent("panelremoved", this, panel);
33814         return panel;
33815     },
33816
33817     /**
33818      * Returns the TabPanel component used by this region
33819      * @return {Roo.TabPanel}
33820      */
33821     getTabs : function(){
33822         return this.tabs;
33823     },
33824
33825     createTool : function(parentEl, className){
33826         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33827             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
33828         btn.addClassOnOver("x-layout-tools-button-over");
33829         return btn;
33830     }
33831 });/*
33832  * Based on:
33833  * Ext JS Library 1.1.1
33834  * Copyright(c) 2006-2007, Ext JS, LLC.
33835  *
33836  * Originally Released Under LGPL - original licence link has changed is not relivant.
33837  *
33838  * Fork - LGPL
33839  * <script type="text/javascript">
33840  */
33841  
33842
33843
33844 /**
33845  * @class Roo.SplitLayoutRegion
33846  * @extends Roo.LayoutRegion
33847  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33848  */
33849 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
33850     this.cursor = cursor;
33851     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
33852 };
33853
33854 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
33855     splitTip : "Drag to resize.",
33856     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33857     useSplitTips : false,
33858
33859     applyConfig : function(config){
33860         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
33861         if(config.split){
33862             if(!this.split){
33863                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
33864                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
33865                 /** The SplitBar for this region 
33866                 * @type Roo.SplitBar */
33867                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
33868                 this.split.on("moved", this.onSplitMove, this);
33869                 this.split.useShim = config.useShim === true;
33870                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33871                 if(this.useSplitTips){
33872                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33873                 }
33874                 if(config.collapsible){
33875                     this.split.el.on("dblclick", this.collapse,  this);
33876                 }
33877             }
33878             if(typeof config.minSize != "undefined"){
33879                 this.split.minSize = config.minSize;
33880             }
33881             if(typeof config.maxSize != "undefined"){
33882                 this.split.maxSize = config.maxSize;
33883             }
33884             if(config.hideWhenEmpty || config.hidden || config.collapsed){
33885                 this.hideSplitter();
33886             }
33887         }
33888     },
33889
33890     getHMaxSize : function(){
33891          var cmax = this.config.maxSize || 10000;
33892          var center = this.mgr.getRegion("center");
33893          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33894     },
33895
33896     getVMaxSize : function(){
33897          var cmax = this.config.maxSize || 10000;
33898          var center = this.mgr.getRegion("center");
33899          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33900     },
33901
33902     onSplitMove : function(split, newSize){
33903         this.fireEvent("resized", this, newSize);
33904     },
33905     
33906     /** 
33907      * Returns the {@link Roo.SplitBar} for this region.
33908      * @return {Roo.SplitBar}
33909      */
33910     getSplitBar : function(){
33911         return this.split;
33912     },
33913     
33914     hide : function(){
33915         this.hideSplitter();
33916         Roo.SplitLayoutRegion.superclass.hide.call(this);
33917     },
33918
33919     hideSplitter : function(){
33920         if(this.split){
33921             this.split.el.setLocation(-2000,-2000);
33922             this.split.el.hide();
33923         }
33924     },
33925
33926     show : function(){
33927         if(this.split){
33928             this.split.el.show();
33929         }
33930         Roo.SplitLayoutRegion.superclass.show.call(this);
33931     },
33932     
33933     beforeSlide: function(){
33934         if(Roo.isGecko){// firefox overflow auto bug workaround
33935             this.bodyEl.clip();
33936             if(this.tabs) this.tabs.bodyEl.clip();
33937             if(this.activePanel){
33938                 this.activePanel.getEl().clip();
33939                 
33940                 if(this.activePanel.beforeSlide){
33941                     this.activePanel.beforeSlide();
33942                 }
33943             }
33944         }
33945     },
33946     
33947     afterSlide : function(){
33948         if(Roo.isGecko){// firefox overflow auto bug workaround
33949             this.bodyEl.unclip();
33950             if(this.tabs) this.tabs.bodyEl.unclip();
33951             if(this.activePanel){
33952                 this.activePanel.getEl().unclip();
33953                 if(this.activePanel.afterSlide){
33954                     this.activePanel.afterSlide();
33955                 }
33956             }
33957         }
33958     },
33959
33960     initAutoHide : function(){
33961         if(this.autoHide !== false){
33962             if(!this.autoHideHd){
33963                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33964                 this.autoHideHd = {
33965                     "mouseout": function(e){
33966                         if(!e.within(this.el, true)){
33967                             st.delay(500);
33968                         }
33969                     },
33970                     "mouseover" : function(e){
33971                         st.cancel();
33972                     },
33973                     scope : this
33974                 };
33975             }
33976             this.el.on(this.autoHideHd);
33977         }
33978     },
33979
33980     clearAutoHide : function(){
33981         if(this.autoHide !== false){
33982             this.el.un("mouseout", this.autoHideHd.mouseout);
33983             this.el.un("mouseover", this.autoHideHd.mouseover);
33984         }
33985     },
33986
33987     clearMonitor : function(){
33988         Roo.get(document).un("click", this.slideInIf, this);
33989     },
33990
33991     // these names are backwards but not changed for compat
33992     slideOut : function(){
33993         if(this.isSlid || this.el.hasActiveFx()){
33994             return;
33995         }
33996         this.isSlid = true;
33997         if(this.collapseBtn){
33998             this.collapseBtn.hide();
33999         }
34000         this.closeBtnState = this.closeBtn.getStyle('display');
34001         this.closeBtn.hide();
34002         if(this.stickBtn){
34003             this.stickBtn.show();
34004         }
34005         this.el.show();
34006         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34007         this.beforeSlide();
34008         this.el.setStyle("z-index", 10001);
34009         this.el.slideIn(this.getSlideAnchor(), {
34010             callback: function(){
34011                 this.afterSlide();
34012                 this.initAutoHide();
34013                 Roo.get(document).on("click", this.slideInIf, this);
34014                 this.fireEvent("slideshow", this);
34015             },
34016             scope: this,
34017             block: true
34018         });
34019     },
34020
34021     afterSlideIn : function(){
34022         this.clearAutoHide();
34023         this.isSlid = false;
34024         this.clearMonitor();
34025         this.el.setStyle("z-index", "");
34026         if(this.collapseBtn){
34027             this.collapseBtn.show();
34028         }
34029         this.closeBtn.setStyle('display', this.closeBtnState);
34030         if(this.stickBtn){
34031             this.stickBtn.hide();
34032         }
34033         this.fireEvent("slidehide", this);
34034     },
34035
34036     slideIn : function(cb){
34037         if(!this.isSlid || this.el.hasActiveFx()){
34038             Roo.callback(cb);
34039             return;
34040         }
34041         this.isSlid = false;
34042         this.beforeSlide();
34043         this.el.slideOut(this.getSlideAnchor(), {
34044             callback: function(){
34045                 this.el.setLeftTop(-10000, -10000);
34046                 this.afterSlide();
34047                 this.afterSlideIn();
34048                 Roo.callback(cb);
34049             },
34050             scope: this,
34051             block: true
34052         });
34053     },
34054     
34055     slideInIf : function(e){
34056         if(!e.within(this.el)){
34057             this.slideIn();
34058         }
34059     },
34060
34061     animateCollapse : function(){
34062         this.beforeSlide();
34063         this.el.setStyle("z-index", 20000);
34064         var anchor = this.getSlideAnchor();
34065         this.el.slideOut(anchor, {
34066             callback : function(){
34067                 this.el.setStyle("z-index", "");
34068                 this.collapsedEl.slideIn(anchor, {duration:.3});
34069                 this.afterSlide();
34070                 this.el.setLocation(-10000,-10000);
34071                 this.el.hide();
34072                 this.fireEvent("collapsed", this);
34073             },
34074             scope: this,
34075             block: true
34076         });
34077     },
34078
34079     animateExpand : function(){
34080         this.beforeSlide();
34081         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34082         this.el.setStyle("z-index", 20000);
34083         this.collapsedEl.hide({
34084             duration:.1
34085         });
34086         this.el.slideIn(this.getSlideAnchor(), {
34087             callback : function(){
34088                 this.el.setStyle("z-index", "");
34089                 this.afterSlide();
34090                 if(this.split){
34091                     this.split.el.show();
34092                 }
34093                 this.fireEvent("invalidated", this);
34094                 this.fireEvent("expanded", this);
34095             },
34096             scope: this,
34097             block: true
34098         });
34099     },
34100
34101     anchors : {
34102         "west" : "left",
34103         "east" : "right",
34104         "north" : "top",
34105         "south" : "bottom"
34106     },
34107
34108     sanchors : {
34109         "west" : "l",
34110         "east" : "r",
34111         "north" : "t",
34112         "south" : "b"
34113     },
34114
34115     canchors : {
34116         "west" : "tl-tr",
34117         "east" : "tr-tl",
34118         "north" : "tl-bl",
34119         "south" : "bl-tl"
34120     },
34121
34122     getAnchor : function(){
34123         return this.anchors[this.position];
34124     },
34125
34126     getCollapseAnchor : function(){
34127         return this.canchors[this.position];
34128     },
34129
34130     getSlideAnchor : function(){
34131         return this.sanchors[this.position];
34132     },
34133
34134     getAlignAdj : function(){
34135         var cm = this.cmargins;
34136         switch(this.position){
34137             case "west":
34138                 return [0, 0];
34139             break;
34140             case "east":
34141                 return [0, 0];
34142             break;
34143             case "north":
34144                 return [0, 0];
34145             break;
34146             case "south":
34147                 return [0, 0];
34148             break;
34149         }
34150     },
34151
34152     getExpandAdj : function(){
34153         var c = this.collapsedEl, cm = this.cmargins;
34154         switch(this.position){
34155             case "west":
34156                 return [-(cm.right+c.getWidth()+cm.left), 0];
34157             break;
34158             case "east":
34159                 return [cm.right+c.getWidth()+cm.left, 0];
34160             break;
34161             case "north":
34162                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34163             break;
34164             case "south":
34165                 return [0, cm.top+cm.bottom+c.getHeight()];
34166             break;
34167         }
34168     }
34169 });/*
34170  * Based on:
34171  * Ext JS Library 1.1.1
34172  * Copyright(c) 2006-2007, Ext JS, LLC.
34173  *
34174  * Originally Released Under LGPL - original licence link has changed is not relivant.
34175  *
34176  * Fork - LGPL
34177  * <script type="text/javascript">
34178  */
34179 /*
34180  * These classes are private internal classes
34181  */
34182 Roo.CenterLayoutRegion = function(mgr, config){
34183     Roo.LayoutRegion.call(this, mgr, config, "center");
34184     this.visible = true;
34185     this.minWidth = config.minWidth || 20;
34186     this.minHeight = config.minHeight || 20;
34187 };
34188
34189 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34190     hide : function(){
34191         // center panel can't be hidden
34192     },
34193     
34194     show : function(){
34195         // center panel can't be hidden
34196     },
34197     
34198     getMinWidth: function(){
34199         return this.minWidth;
34200     },
34201     
34202     getMinHeight: function(){
34203         return this.minHeight;
34204     }
34205 });
34206
34207
34208 Roo.NorthLayoutRegion = function(mgr, config){
34209     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34210     if(this.split){
34211         this.split.placement = Roo.SplitBar.TOP;
34212         this.split.orientation = Roo.SplitBar.VERTICAL;
34213         this.split.el.addClass("x-layout-split-v");
34214     }
34215     var size = config.initialSize || config.height;
34216     if(typeof size != "undefined"){
34217         this.el.setHeight(size);
34218     }
34219 };
34220 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34221     orientation: Roo.SplitBar.VERTICAL,
34222     getBox : function(){
34223         if(this.collapsed){
34224             return this.collapsedEl.getBox();
34225         }
34226         var box = this.el.getBox();
34227         if(this.split){
34228             box.height += this.split.el.getHeight();
34229         }
34230         return box;
34231     },
34232     
34233     updateBox : function(box){
34234         if(this.split && !this.collapsed){
34235             box.height -= this.split.el.getHeight();
34236             this.split.el.setLeft(box.x);
34237             this.split.el.setTop(box.y+box.height);
34238             this.split.el.setWidth(box.width);
34239         }
34240         if(this.collapsed){
34241             this.updateBody(box.width, null);
34242         }
34243         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34244     }
34245 });
34246
34247 Roo.SouthLayoutRegion = function(mgr, config){
34248     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34249     if(this.split){
34250         this.split.placement = Roo.SplitBar.BOTTOM;
34251         this.split.orientation = Roo.SplitBar.VERTICAL;
34252         this.split.el.addClass("x-layout-split-v");
34253     }
34254     var size = config.initialSize || config.height;
34255     if(typeof size != "undefined"){
34256         this.el.setHeight(size);
34257     }
34258 };
34259 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34260     orientation: Roo.SplitBar.VERTICAL,
34261     getBox : function(){
34262         if(this.collapsed){
34263             return this.collapsedEl.getBox();
34264         }
34265         var box = this.el.getBox();
34266         if(this.split){
34267             var sh = this.split.el.getHeight();
34268             box.height += sh;
34269             box.y -= sh;
34270         }
34271         return box;
34272     },
34273     
34274     updateBox : function(box){
34275         if(this.split && !this.collapsed){
34276             var sh = this.split.el.getHeight();
34277             box.height -= sh;
34278             box.y += sh;
34279             this.split.el.setLeft(box.x);
34280             this.split.el.setTop(box.y-sh);
34281             this.split.el.setWidth(box.width);
34282         }
34283         if(this.collapsed){
34284             this.updateBody(box.width, null);
34285         }
34286         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34287     }
34288 });
34289
34290 Roo.EastLayoutRegion = function(mgr, config){
34291     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34292     if(this.split){
34293         this.split.placement = Roo.SplitBar.RIGHT;
34294         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34295         this.split.el.addClass("x-layout-split-h");
34296     }
34297     var size = config.initialSize || config.width;
34298     if(typeof size != "undefined"){
34299         this.el.setWidth(size);
34300     }
34301 };
34302 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34303     orientation: Roo.SplitBar.HORIZONTAL,
34304     getBox : function(){
34305         if(this.collapsed){
34306             return this.collapsedEl.getBox();
34307         }
34308         var box = this.el.getBox();
34309         if(this.split){
34310             var sw = this.split.el.getWidth();
34311             box.width += sw;
34312             box.x -= sw;
34313         }
34314         return box;
34315     },
34316
34317     updateBox : function(box){
34318         if(this.split && !this.collapsed){
34319             var sw = this.split.el.getWidth();
34320             box.width -= sw;
34321             this.split.el.setLeft(box.x);
34322             this.split.el.setTop(box.y);
34323             this.split.el.setHeight(box.height);
34324             box.x += sw;
34325         }
34326         if(this.collapsed){
34327             this.updateBody(null, box.height);
34328         }
34329         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34330     }
34331 });
34332
34333 Roo.WestLayoutRegion = function(mgr, config){
34334     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34335     if(this.split){
34336         this.split.placement = Roo.SplitBar.LEFT;
34337         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34338         this.split.el.addClass("x-layout-split-h");
34339     }
34340     var size = config.initialSize || config.width;
34341     if(typeof size != "undefined"){
34342         this.el.setWidth(size);
34343     }
34344 };
34345 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34346     orientation: Roo.SplitBar.HORIZONTAL,
34347     getBox : function(){
34348         if(this.collapsed){
34349             return this.collapsedEl.getBox();
34350         }
34351         var box = this.el.getBox();
34352         if(this.split){
34353             box.width += this.split.el.getWidth();
34354         }
34355         return box;
34356     },
34357     
34358     updateBox : function(box){
34359         if(this.split && !this.collapsed){
34360             var sw = this.split.el.getWidth();
34361             box.width -= sw;
34362             this.split.el.setLeft(box.x+box.width);
34363             this.split.el.setTop(box.y);
34364             this.split.el.setHeight(box.height);
34365         }
34366         if(this.collapsed){
34367             this.updateBody(null, box.height);
34368         }
34369         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34370     }
34371 });
34372 /*
34373  * Based on:
34374  * Ext JS Library 1.1.1
34375  * Copyright(c) 2006-2007, Ext JS, LLC.
34376  *
34377  * Originally Released Under LGPL - original licence link has changed is not relivant.
34378  *
34379  * Fork - LGPL
34380  * <script type="text/javascript">
34381  */
34382  
34383  
34384 /*
34385  * Private internal class for reading and applying state
34386  */
34387 Roo.LayoutStateManager = function(layout){
34388      // default empty state
34389      this.state = {
34390         north: {},
34391         south: {},
34392         east: {},
34393         west: {}       
34394     };
34395 };
34396
34397 Roo.LayoutStateManager.prototype = {
34398     init : function(layout, provider){
34399         this.provider = provider;
34400         var state = provider.get(layout.id+"-layout-state");
34401         if(state){
34402             var wasUpdating = layout.isUpdating();
34403             if(!wasUpdating){
34404                 layout.beginUpdate();
34405             }
34406             for(var key in state){
34407                 if(typeof state[key] != "function"){
34408                     var rstate = state[key];
34409                     var r = layout.getRegion(key);
34410                     if(r && rstate){
34411                         if(rstate.size){
34412                             r.resizeTo(rstate.size);
34413                         }
34414                         if(rstate.collapsed == true){
34415                             r.collapse(true);
34416                         }else{
34417                             r.expand(null, true);
34418                         }
34419                     }
34420                 }
34421             }
34422             if(!wasUpdating){
34423                 layout.endUpdate();
34424             }
34425             this.state = state; 
34426         }
34427         this.layout = layout;
34428         layout.on("regionresized", this.onRegionResized, this);
34429         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34430         layout.on("regionexpanded", this.onRegionExpanded, this);
34431     },
34432     
34433     storeState : function(){
34434         this.provider.set(this.layout.id+"-layout-state", this.state);
34435     },
34436     
34437     onRegionResized : function(region, newSize){
34438         this.state[region.getPosition()].size = newSize;
34439         this.storeState();
34440     },
34441     
34442     onRegionCollapsed : function(region){
34443         this.state[region.getPosition()].collapsed = true;
34444         this.storeState();
34445     },
34446     
34447     onRegionExpanded : function(region){
34448         this.state[region.getPosition()].collapsed = false;
34449         this.storeState();
34450     }
34451 };/*
34452  * Based on:
34453  * Ext JS Library 1.1.1
34454  * Copyright(c) 2006-2007, Ext JS, LLC.
34455  *
34456  * Originally Released Under LGPL - original licence link has changed is not relivant.
34457  *
34458  * Fork - LGPL
34459  * <script type="text/javascript">
34460  */
34461 /**
34462  * @class Roo.ContentPanel
34463  * @extends Roo.util.Observable
34464  * A basic ContentPanel element.
34465  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34466  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34467  * @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
34468  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34469  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34470  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34471  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34472  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34473  * @cfg {String} title          The title for this panel
34474  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34475  * @cfg {String} url            Calls {@link #setUrl} with this value
34476  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34477  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34478  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34479  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34480
34481  * @constructor
34482  * Create a new ContentPanel.
34483  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34484  * @param {String/Object} config A string to set only the title or a config object
34485  * @param {String} content (optional) Set the HTML content for this panel
34486  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34487  */
34488 Roo.ContentPanel = function(el, config, content){
34489     
34490      
34491     /*
34492     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34493         config = el;
34494         el = Roo.id();
34495     }
34496     if (config && config.parentLayout) { 
34497         el = config.parentLayout.el.createChild(); 
34498     }
34499     */
34500     if(el.autoCreate){ // xtype is available if this is called from factory
34501         config = el;
34502         el = Roo.id();
34503     }
34504     this.el = Roo.get(el);
34505     if(!this.el && config && config.autoCreate){
34506         if(typeof config.autoCreate == "object"){
34507             if(!config.autoCreate.id){
34508                 config.autoCreate.id = config.id||el;
34509             }
34510             this.el = Roo.DomHelper.append(document.body,
34511                         config.autoCreate, true);
34512         }else{
34513             this.el = Roo.DomHelper.append(document.body,
34514                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34515         }
34516     }
34517     this.closable = false;
34518     this.loaded = false;
34519     this.active = false;
34520     if(typeof config == "string"){
34521         this.title = config;
34522     }else{
34523         Roo.apply(this, config);
34524     }
34525     
34526     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34527         this.wrapEl = this.el.wrap();
34528         this.toolbar.container = this.el.insertSibling(false, 'before');
34529         this.toolbar = new Roo.Toolbar(this.toolbar);
34530     }
34531     
34532     // xtype created footer. - not sure if will work as we normally have to render first..
34533     if (this.footer && !this.footer.el && this.footer.xtype) {
34534         if (!this.wrapEl) {
34535             this.wrapEl = this.el.wrap();
34536         }
34537     
34538         this.footer.container = this.wrapEl.createChild();
34539          
34540         this.footer = Roo.factory(this.footer, Roo);
34541         
34542     }
34543     
34544     if(this.resizeEl){
34545         this.resizeEl = Roo.get(this.resizeEl, true);
34546     }else{
34547         this.resizeEl = this.el;
34548     }
34549     // handle view.xtype
34550     
34551  
34552     
34553     
34554     this.addEvents({
34555         /**
34556          * @event activate
34557          * Fires when this panel is activated. 
34558          * @param {Roo.ContentPanel} this
34559          */
34560         "activate" : true,
34561         /**
34562          * @event deactivate
34563          * Fires when this panel is activated. 
34564          * @param {Roo.ContentPanel} this
34565          */
34566         "deactivate" : true,
34567
34568         /**
34569          * @event resize
34570          * Fires when this panel is resized if fitToFrame is true.
34571          * @param {Roo.ContentPanel} this
34572          * @param {Number} width The width after any component adjustments
34573          * @param {Number} height The height after any component adjustments
34574          */
34575         "resize" : true,
34576         
34577          /**
34578          * @event render
34579          * Fires when this tab is created
34580          * @param {Roo.ContentPanel} this
34581          */
34582         "render" : true
34583         
34584         
34585         
34586     });
34587     
34588
34589     
34590     
34591     if(this.autoScroll){
34592         this.resizeEl.setStyle("overflow", "auto");
34593     } else {
34594         // fix randome scrolling
34595         this.el.on('scroll', function() {
34596             Roo.log('fix random scolling');
34597             this.scrollTo('top',0); 
34598         });
34599     }
34600     content = content || this.content;
34601     if(content){
34602         this.setContent(content);
34603     }
34604     if(config && config.url){
34605         this.setUrl(this.url, this.params, this.loadOnce);
34606     }
34607     
34608     
34609     
34610     Roo.ContentPanel.superclass.constructor.call(this);
34611     
34612     if (this.view && typeof(this.view.xtype) != 'undefined') {
34613         this.view.el = this.el.appendChild(document.createElement("div"));
34614         this.view = Roo.factory(this.view); 
34615         this.view.render  &&  this.view.render(false, '');  
34616     }
34617     
34618     
34619     this.fireEvent('render', this);
34620 };
34621
34622 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34623     tabTip:'',
34624     setRegion : function(region){
34625         this.region = region;
34626         if(region){
34627            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34628         }else{
34629            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34630         } 
34631     },
34632     
34633     /**
34634      * Returns the toolbar for this Panel if one was configured. 
34635      * @return {Roo.Toolbar} 
34636      */
34637     getToolbar : function(){
34638         return this.toolbar;
34639     },
34640     
34641     setActiveState : function(active){
34642         this.active = active;
34643         if(!active){
34644             this.fireEvent("deactivate", this);
34645         }else{
34646             this.fireEvent("activate", this);
34647         }
34648     },
34649     /**
34650      * Updates this panel's element
34651      * @param {String} content The new content
34652      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34653     */
34654     setContent : function(content, loadScripts){
34655         this.el.update(content, loadScripts);
34656     },
34657
34658     ignoreResize : function(w, h){
34659         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34660             return true;
34661         }else{
34662             this.lastSize = {width: w, height: h};
34663             return false;
34664         }
34665     },
34666     /**
34667      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34668      * @return {Roo.UpdateManager} The UpdateManager
34669      */
34670     getUpdateManager : function(){
34671         return this.el.getUpdateManager();
34672     },
34673      /**
34674      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34675      * @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:
34676 <pre><code>
34677 panel.load({
34678     url: "your-url.php",
34679     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34680     callback: yourFunction,
34681     scope: yourObject, //(optional scope)
34682     discardUrl: false,
34683     nocache: false,
34684     text: "Loading...",
34685     timeout: 30,
34686     scripts: false
34687 });
34688 </code></pre>
34689      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34690      * 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.
34691      * @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}
34692      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34693      * @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.
34694      * @return {Roo.ContentPanel} this
34695      */
34696     load : function(){
34697         var um = this.el.getUpdateManager();
34698         um.update.apply(um, arguments);
34699         return this;
34700     },
34701
34702
34703     /**
34704      * 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.
34705      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34706      * @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)
34707      * @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)
34708      * @return {Roo.UpdateManager} The UpdateManager
34709      */
34710     setUrl : function(url, params, loadOnce){
34711         if(this.refreshDelegate){
34712             this.removeListener("activate", this.refreshDelegate);
34713         }
34714         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34715         this.on("activate", this.refreshDelegate);
34716         return this.el.getUpdateManager();
34717     },
34718     
34719     _handleRefresh : function(url, params, loadOnce){
34720         if(!loadOnce || !this.loaded){
34721             var updater = this.el.getUpdateManager();
34722             updater.update(url, params, this._setLoaded.createDelegate(this));
34723         }
34724     },
34725     
34726     _setLoaded : function(){
34727         this.loaded = true;
34728     }, 
34729     
34730     /**
34731      * Returns this panel's id
34732      * @return {String} 
34733      */
34734     getId : function(){
34735         return this.el.id;
34736     },
34737     
34738     /** 
34739      * Returns this panel's element - used by regiosn to add.
34740      * @return {Roo.Element} 
34741      */
34742     getEl : function(){
34743         return this.wrapEl || this.el;
34744     },
34745     
34746     adjustForComponents : function(width, height)
34747     {
34748         //Roo.log('adjustForComponents ');
34749         if(this.resizeEl != this.el){
34750             width -= this.el.getFrameWidth('lr');
34751             height -= this.el.getFrameWidth('tb');
34752         }
34753         if(this.toolbar){
34754             var te = this.toolbar.getEl();
34755             height -= te.getHeight();
34756             te.setWidth(width);
34757         }
34758         if(this.footer){
34759             var te = this.footer.getEl();
34760             Roo.log("footer:" + te.getHeight());
34761             
34762             height -= te.getHeight();
34763             te.setWidth(width);
34764         }
34765         
34766         
34767         if(this.adjustments){
34768             width += this.adjustments[0];
34769             height += this.adjustments[1];
34770         }
34771         return {"width": width, "height": height};
34772     },
34773     
34774     setSize : function(width, height){
34775         if(this.fitToFrame && !this.ignoreResize(width, height)){
34776             if(this.fitContainer && this.resizeEl != this.el){
34777                 this.el.setSize(width, height);
34778             }
34779             var size = this.adjustForComponents(width, height);
34780             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34781             this.fireEvent('resize', this, size.width, size.height);
34782         }
34783     },
34784     
34785     /**
34786      * Returns this panel's title
34787      * @return {String} 
34788      */
34789     getTitle : function(){
34790         return this.title;
34791     },
34792     
34793     /**
34794      * Set this panel's title
34795      * @param {String} title
34796      */
34797     setTitle : function(title){
34798         this.title = title;
34799         if(this.region){
34800             this.region.updatePanelTitle(this, title);
34801         }
34802     },
34803     
34804     /**
34805      * Returns true is this panel was configured to be closable
34806      * @return {Boolean} 
34807      */
34808     isClosable : function(){
34809         return this.closable;
34810     },
34811     
34812     beforeSlide : function(){
34813         this.el.clip();
34814         this.resizeEl.clip();
34815     },
34816     
34817     afterSlide : function(){
34818         this.el.unclip();
34819         this.resizeEl.unclip();
34820     },
34821     
34822     /**
34823      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34824      *   Will fail silently if the {@link #setUrl} method has not been called.
34825      *   This does not activate the panel, just updates its content.
34826      */
34827     refresh : function(){
34828         if(this.refreshDelegate){
34829            this.loaded = false;
34830            this.refreshDelegate();
34831         }
34832     },
34833     
34834     /**
34835      * Destroys this panel
34836      */
34837     destroy : function(){
34838         this.el.removeAllListeners();
34839         var tempEl = document.createElement("span");
34840         tempEl.appendChild(this.el.dom);
34841         tempEl.innerHTML = "";
34842         this.el.remove();
34843         this.el = null;
34844     },
34845     
34846     /**
34847      * form - if the content panel contains a form - this is a reference to it.
34848      * @type {Roo.form.Form}
34849      */
34850     form : false,
34851     /**
34852      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34853      *    This contains a reference to it.
34854      * @type {Roo.View}
34855      */
34856     view : false,
34857     
34858       /**
34859      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34860      * <pre><code>
34861
34862 layout.addxtype({
34863        xtype : 'Form',
34864        items: [ .... ]
34865    }
34866 );
34867
34868 </code></pre>
34869      * @param {Object} cfg Xtype definition of item to add.
34870      */
34871     
34872     addxtype : function(cfg) {
34873         // add form..
34874         if (cfg.xtype.match(/^Form$/)) {
34875             
34876             var el;
34877             //if (this.footer) {
34878             //    el = this.footer.container.insertSibling(false, 'before');
34879             //} else {
34880                 el = this.el.createChild();
34881             //}
34882
34883             this.form = new  Roo.form.Form(cfg);
34884             
34885             
34886             if ( this.form.allItems.length) this.form.render(el.dom);
34887             return this.form;
34888         }
34889         // should only have one of theses..
34890         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34891             // views.. should not be just added - used named prop 'view''
34892             
34893             cfg.el = this.el.appendChild(document.createElement("div"));
34894             // factory?
34895             
34896             var ret = new Roo.factory(cfg);
34897              
34898              ret.render && ret.render(false, ''); // render blank..
34899             this.view = ret;
34900             return ret;
34901         }
34902         return false;
34903     }
34904 });
34905
34906 /**
34907  * @class Roo.GridPanel
34908  * @extends Roo.ContentPanel
34909  * @constructor
34910  * Create a new GridPanel.
34911  * @param {Roo.grid.Grid} grid The grid for this panel
34912  * @param {String/Object} config A string to set only the panel's title, or a config object
34913  */
34914 Roo.GridPanel = function(grid, config){
34915     
34916   
34917     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34918         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
34919         
34920     this.wrapper.dom.appendChild(grid.getGridEl().dom);
34921     
34922     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
34923     
34924     if(this.toolbar){
34925         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
34926     }
34927     // xtype created footer. - not sure if will work as we normally have to render first..
34928     if (this.footer && !this.footer.el && this.footer.xtype) {
34929         
34930         this.footer.container = this.grid.getView().getFooterPanel(true);
34931         this.footer.dataSource = this.grid.dataSource;
34932         this.footer = Roo.factory(this.footer, Roo);
34933         
34934     }
34935     
34936     grid.monitorWindowResize = false; // turn off autosizing
34937     grid.autoHeight = false;
34938     grid.autoWidth = false;
34939     this.grid = grid;
34940     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
34941 };
34942
34943 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
34944     getId : function(){
34945         return this.grid.id;
34946     },
34947     
34948     /**
34949      * Returns the grid for this panel
34950      * @return {Roo.grid.Grid} 
34951      */
34952     getGrid : function(){
34953         return this.grid;    
34954     },
34955     
34956     setSize : function(width, height){
34957         if(!this.ignoreResize(width, height)){
34958             var grid = this.grid;
34959             var size = this.adjustForComponents(width, height);
34960             grid.getGridEl().setSize(size.width, size.height);
34961             grid.autoSize();
34962         }
34963     },
34964     
34965     beforeSlide : function(){
34966         this.grid.getView().scroller.clip();
34967     },
34968     
34969     afterSlide : function(){
34970         this.grid.getView().scroller.unclip();
34971     },
34972     
34973     destroy : function(){
34974         this.grid.destroy();
34975         delete this.grid;
34976         Roo.GridPanel.superclass.destroy.call(this); 
34977     }
34978 });
34979
34980
34981 /**
34982  * @class Roo.NestedLayoutPanel
34983  * @extends Roo.ContentPanel
34984  * @constructor
34985  * Create a new NestedLayoutPanel.
34986  * 
34987  * 
34988  * @param {Roo.BorderLayout} layout The layout for this panel
34989  * @param {String/Object} config A string to set only the title or a config object
34990  */
34991 Roo.NestedLayoutPanel = function(layout, config)
34992 {
34993     // construct with only one argument..
34994     /* FIXME - implement nicer consturctors
34995     if (layout.layout) {
34996         config = layout;
34997         layout = config.layout;
34998         delete config.layout;
34999     }
35000     if (layout.xtype && !layout.getEl) {
35001         // then layout needs constructing..
35002         layout = Roo.factory(layout, Roo);
35003     }
35004     */
35005     
35006     
35007     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35008     
35009     layout.monitorWindowResize = false; // turn off autosizing
35010     this.layout = layout;
35011     this.layout.getEl().addClass("x-layout-nested-layout");
35012     
35013     
35014     
35015     
35016 };
35017
35018 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35019
35020     setSize : function(width, height){
35021         if(!this.ignoreResize(width, height)){
35022             var size = this.adjustForComponents(width, height);
35023             var el = this.layout.getEl();
35024             el.setSize(size.width, size.height);
35025             var touch = el.dom.offsetWidth;
35026             this.layout.layout();
35027             // ie requires a double layout on the first pass
35028             if(Roo.isIE && !this.initialized){
35029                 this.initialized = true;
35030                 this.layout.layout();
35031             }
35032         }
35033     },
35034     
35035     // activate all subpanels if not currently active..
35036     
35037     setActiveState : function(active){
35038         this.active = active;
35039         if(!active){
35040             this.fireEvent("deactivate", this);
35041             return;
35042         }
35043         
35044         this.fireEvent("activate", this);
35045         // not sure if this should happen before or after..
35046         if (!this.layout) {
35047             return; // should not happen..
35048         }
35049         var reg = false;
35050         for (var r in this.layout.regions) {
35051             reg = this.layout.getRegion(r);
35052             if (reg.getActivePanel()) {
35053                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35054                 reg.setActivePanel(reg.getActivePanel());
35055                 continue;
35056             }
35057             if (!reg.panels.length) {
35058                 continue;
35059             }
35060             reg.showPanel(reg.getPanel(0));
35061         }
35062         
35063         
35064         
35065         
35066     },
35067     
35068     /**
35069      * Returns the nested BorderLayout for this panel
35070      * @return {Roo.BorderLayout} 
35071      */
35072     getLayout : function(){
35073         return this.layout;
35074     },
35075     
35076      /**
35077      * Adds a xtype elements to the layout of the nested panel
35078      * <pre><code>
35079
35080 panel.addxtype({
35081        xtype : 'ContentPanel',
35082        region: 'west',
35083        items: [ .... ]
35084    }
35085 );
35086
35087 panel.addxtype({
35088         xtype : 'NestedLayoutPanel',
35089         region: 'west',
35090         layout: {
35091            center: { },
35092            west: { }   
35093         },
35094         items : [ ... list of content panels or nested layout panels.. ]
35095    }
35096 );
35097 </code></pre>
35098      * @param {Object} cfg Xtype definition of item to add.
35099      */
35100     addxtype : function(cfg) {
35101         return this.layout.addxtype(cfg);
35102     
35103     }
35104 });
35105
35106 Roo.ScrollPanel = function(el, config, content){
35107     config = config || {};
35108     config.fitToFrame = true;
35109     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35110     
35111     this.el.dom.style.overflow = "hidden";
35112     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35113     this.el.removeClass("x-layout-inactive-content");
35114     this.el.on("mousewheel", this.onWheel, this);
35115
35116     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35117     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35118     up.unselectable(); down.unselectable();
35119     up.on("click", this.scrollUp, this);
35120     down.on("click", this.scrollDown, this);
35121     up.addClassOnOver("x-scroller-btn-over");
35122     down.addClassOnOver("x-scroller-btn-over");
35123     up.addClassOnClick("x-scroller-btn-click");
35124     down.addClassOnClick("x-scroller-btn-click");
35125     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35126
35127     this.resizeEl = this.el;
35128     this.el = wrap; this.up = up; this.down = down;
35129 };
35130
35131 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35132     increment : 100,
35133     wheelIncrement : 5,
35134     scrollUp : function(){
35135         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35136     },
35137
35138     scrollDown : function(){
35139         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35140     },
35141
35142     afterScroll : function(){
35143         var el = this.resizeEl;
35144         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35145         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35146         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35147     },
35148
35149     setSize : function(){
35150         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35151         this.afterScroll();
35152     },
35153
35154     onWheel : function(e){
35155         var d = e.getWheelDelta();
35156         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35157         this.afterScroll();
35158         e.stopEvent();
35159     },
35160
35161     setContent : function(content, loadScripts){
35162         this.resizeEl.update(content, loadScripts);
35163     }
35164
35165 });
35166
35167
35168
35169
35170
35171
35172
35173
35174
35175 /**
35176  * @class Roo.TreePanel
35177  * @extends Roo.ContentPanel
35178  * @constructor
35179  * Create a new TreePanel. - defaults to fit/scoll contents.
35180  * @param {String/Object} config A string to set only the panel's title, or a config object
35181  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35182  */
35183 Roo.TreePanel = function(config){
35184     var el = config.el;
35185     var tree = config.tree;
35186     delete config.tree; 
35187     delete config.el; // hopefull!
35188     
35189     // wrapper for IE7 strict & safari scroll issue
35190     
35191     var treeEl = el.createChild();
35192     config.resizeEl = treeEl;
35193     
35194     
35195     
35196     Roo.TreePanel.superclass.constructor.call(this, el, config);
35197  
35198  
35199     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35200     //console.log(tree);
35201     this.on('activate', function()
35202     {
35203         if (this.tree.rendered) {
35204             return;
35205         }
35206         //console.log('render tree');
35207         this.tree.render();
35208     });
35209     // this should not be needed.. - it's actually the 'el' that resizes?
35210     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35211     
35212     //this.on('resize',  function (cp, w, h) {
35213     //        this.tree.innerCt.setWidth(w);
35214     //        this.tree.innerCt.setHeight(h);
35215     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35216     //});
35217
35218         
35219     
35220 };
35221
35222 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35223     fitToFrame : true,
35224     autoScroll : true
35225 });
35226
35227
35228
35229
35230
35231
35232
35233
35234
35235
35236
35237 /*
35238  * Based on:
35239  * Ext JS Library 1.1.1
35240  * Copyright(c) 2006-2007, Ext JS, LLC.
35241  *
35242  * Originally Released Under LGPL - original licence link has changed is not relivant.
35243  *
35244  * Fork - LGPL
35245  * <script type="text/javascript">
35246  */
35247  
35248
35249 /**
35250  * @class Roo.ReaderLayout
35251  * @extends Roo.BorderLayout
35252  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35253  * center region containing two nested regions (a top one for a list view and one for item preview below),
35254  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35255  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35256  * expedites the setup of the overall layout and regions for this common application style.
35257  * Example:
35258  <pre><code>
35259 var reader = new Roo.ReaderLayout();
35260 var CP = Roo.ContentPanel;  // shortcut for adding
35261
35262 reader.beginUpdate();
35263 reader.add("north", new CP("north", "North"));
35264 reader.add("west", new CP("west", {title: "West"}));
35265 reader.add("east", new CP("east", {title: "East"}));
35266
35267 reader.regions.listView.add(new CP("listView", "List"));
35268 reader.regions.preview.add(new CP("preview", "Preview"));
35269 reader.endUpdate();
35270 </code></pre>
35271 * @constructor
35272 * Create a new ReaderLayout
35273 * @param {Object} config Configuration options
35274 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35275 * document.body if omitted)
35276 */
35277 Roo.ReaderLayout = function(config, renderTo){
35278     var c = config || {size:{}};
35279     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35280         north: c.north !== false ? Roo.apply({
35281             split:false,
35282             initialSize: 32,
35283             titlebar: false
35284         }, c.north) : false,
35285         west: c.west !== false ? Roo.apply({
35286             split:true,
35287             initialSize: 200,
35288             minSize: 175,
35289             maxSize: 400,
35290             titlebar: true,
35291             collapsible: true,
35292             animate: true,
35293             margins:{left:5,right:0,bottom:5,top:5},
35294             cmargins:{left:5,right:5,bottom:5,top:5}
35295         }, c.west) : false,
35296         east: c.east !== false ? Roo.apply({
35297             split:true,
35298             initialSize: 200,
35299             minSize: 175,
35300             maxSize: 400,
35301             titlebar: true,
35302             collapsible: true,
35303             animate: true,
35304             margins:{left:0,right:5,bottom:5,top:5},
35305             cmargins:{left:5,right:5,bottom:5,top:5}
35306         }, c.east) : false,
35307         center: Roo.apply({
35308             tabPosition: 'top',
35309             autoScroll:false,
35310             closeOnTab: true,
35311             titlebar:false,
35312             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35313         }, c.center)
35314     });
35315
35316     this.el.addClass('x-reader');
35317
35318     this.beginUpdate();
35319
35320     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35321         south: c.preview !== false ? Roo.apply({
35322             split:true,
35323             initialSize: 200,
35324             minSize: 100,
35325             autoScroll:true,
35326             collapsible:true,
35327             titlebar: true,
35328             cmargins:{top:5,left:0, right:0, bottom:0}
35329         }, c.preview) : false,
35330         center: Roo.apply({
35331             autoScroll:false,
35332             titlebar:false,
35333             minHeight:200
35334         }, c.listView)
35335     });
35336     this.add('center', new Roo.NestedLayoutPanel(inner,
35337             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35338
35339     this.endUpdate();
35340
35341     this.regions.preview = inner.getRegion('south');
35342     this.regions.listView = inner.getRegion('center');
35343 };
35344
35345 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35346  * Based on:
35347  * Ext JS Library 1.1.1
35348  * Copyright(c) 2006-2007, Ext JS, LLC.
35349  *
35350  * Originally Released Under LGPL - original licence link has changed is not relivant.
35351  *
35352  * Fork - LGPL
35353  * <script type="text/javascript">
35354  */
35355  
35356 /**
35357  * @class Roo.grid.Grid
35358  * @extends Roo.util.Observable
35359  * This class represents the primary interface of a component based grid control.
35360  * <br><br>Usage:<pre><code>
35361  var grid = new Roo.grid.Grid("my-container-id", {
35362      ds: myDataStore,
35363      cm: myColModel,
35364      selModel: mySelectionModel,
35365      autoSizeColumns: true,
35366      monitorWindowResize: false,
35367      trackMouseOver: true
35368  });
35369  // set any options
35370  grid.render();
35371  * </code></pre>
35372  * <b>Common Problems:</b><br/>
35373  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35374  * element will correct this<br/>
35375  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35376  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35377  * are unpredictable.<br/>
35378  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35379  * grid to calculate dimensions/offsets.<br/>
35380   * @constructor
35381  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35382  * The container MUST have some type of size defined for the grid to fill. The container will be
35383  * automatically set to position relative if it isn't already.
35384  * @param {Object} config A config object that sets properties on this grid.
35385  */
35386 Roo.grid.Grid = function(container, config){
35387         // initialize the container
35388         this.container = Roo.get(container);
35389         this.container.update("");
35390         this.container.setStyle("overflow", "hidden");
35391     this.container.addClass('x-grid-container');
35392
35393     this.id = this.container.id;
35394
35395     Roo.apply(this, config);
35396     // check and correct shorthanded configs
35397     if(this.ds){
35398         this.dataSource = this.ds;
35399         delete this.ds;
35400     }
35401     if(this.cm){
35402         this.colModel = this.cm;
35403         delete this.cm;
35404     }
35405     if(this.sm){
35406         this.selModel = this.sm;
35407         delete this.sm;
35408     }
35409
35410     if (this.selModel) {
35411         this.selModel = Roo.factory(this.selModel, Roo.grid);
35412         this.sm = this.selModel;
35413         this.sm.xmodule = this.xmodule || false;
35414     }
35415     if (typeof(this.colModel.config) == 'undefined') {
35416         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35417         this.cm = this.colModel;
35418         this.cm.xmodule = this.xmodule || false;
35419     }
35420     if (this.dataSource) {
35421         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35422         this.ds = this.dataSource;
35423         this.ds.xmodule = this.xmodule || false;
35424          
35425     }
35426     
35427     
35428     
35429     if(this.width){
35430         this.container.setWidth(this.width);
35431     }
35432
35433     if(this.height){
35434         this.container.setHeight(this.height);
35435     }
35436     /** @private */
35437         this.addEvents({
35438         // raw events
35439         /**
35440          * @event click
35441          * The raw click event for the entire grid.
35442          * @param {Roo.EventObject} e
35443          */
35444         "click" : true,
35445         /**
35446          * @event dblclick
35447          * The raw dblclick event for the entire grid.
35448          * @param {Roo.EventObject} e
35449          */
35450         "dblclick" : true,
35451         /**
35452          * @event contextmenu
35453          * The raw contextmenu event for the entire grid.
35454          * @param {Roo.EventObject} e
35455          */
35456         "contextmenu" : true,
35457         /**
35458          * @event mousedown
35459          * The raw mousedown event for the entire grid.
35460          * @param {Roo.EventObject} e
35461          */
35462         "mousedown" : true,
35463         /**
35464          * @event mouseup
35465          * The raw mouseup event for the entire grid.
35466          * @param {Roo.EventObject} e
35467          */
35468         "mouseup" : true,
35469         /**
35470          * @event mouseover
35471          * The raw mouseover event for the entire grid.
35472          * @param {Roo.EventObject} e
35473          */
35474         "mouseover" : true,
35475         /**
35476          * @event mouseout
35477          * The raw mouseout event for the entire grid.
35478          * @param {Roo.EventObject} e
35479          */
35480         "mouseout" : true,
35481         /**
35482          * @event keypress
35483          * The raw keypress event for the entire grid.
35484          * @param {Roo.EventObject} e
35485          */
35486         "keypress" : true,
35487         /**
35488          * @event keydown
35489          * The raw keydown event for the entire grid.
35490          * @param {Roo.EventObject} e
35491          */
35492         "keydown" : true,
35493
35494         // custom events
35495
35496         /**
35497          * @event cellclick
35498          * Fires when a cell is clicked
35499          * @param {Grid} this
35500          * @param {Number} rowIndex
35501          * @param {Number} columnIndex
35502          * @param {Roo.EventObject} e
35503          */
35504         "cellclick" : true,
35505         /**
35506          * @event celldblclick
35507          * Fires when a cell is double clicked
35508          * @param {Grid} this
35509          * @param {Number} rowIndex
35510          * @param {Number} columnIndex
35511          * @param {Roo.EventObject} e
35512          */
35513         "celldblclick" : true,
35514         /**
35515          * @event rowclick
35516          * Fires when a row is clicked
35517          * @param {Grid} this
35518          * @param {Number} rowIndex
35519          * @param {Roo.EventObject} e
35520          */
35521         "rowclick" : true,
35522         /**
35523          * @event rowdblclick
35524          * Fires when a row is double clicked
35525          * @param {Grid} this
35526          * @param {Number} rowIndex
35527          * @param {Roo.EventObject} e
35528          */
35529         "rowdblclick" : true,
35530         /**
35531          * @event headerclick
35532          * Fires when a header is clicked
35533          * @param {Grid} this
35534          * @param {Number} columnIndex
35535          * @param {Roo.EventObject} e
35536          */
35537         "headerclick" : true,
35538         /**
35539          * @event headerdblclick
35540          * Fires when a header cell is double clicked
35541          * @param {Grid} this
35542          * @param {Number} columnIndex
35543          * @param {Roo.EventObject} e
35544          */
35545         "headerdblclick" : true,
35546         /**
35547          * @event rowcontextmenu
35548          * Fires when a row is right clicked
35549          * @param {Grid} this
35550          * @param {Number} rowIndex
35551          * @param {Roo.EventObject} e
35552          */
35553         "rowcontextmenu" : true,
35554         /**
35555          * @event cellcontextmenu
35556          * Fires when a cell is right clicked
35557          * @param {Grid} this
35558          * @param {Number} rowIndex
35559          * @param {Number} cellIndex
35560          * @param {Roo.EventObject} e
35561          */
35562          "cellcontextmenu" : true,
35563         /**
35564          * @event headercontextmenu
35565          * Fires when a header is right clicked
35566          * @param {Grid} this
35567          * @param {Number} columnIndex
35568          * @param {Roo.EventObject} e
35569          */
35570         "headercontextmenu" : true,
35571         /**
35572          * @event bodyscroll
35573          * Fires when the body element is scrolled
35574          * @param {Number} scrollLeft
35575          * @param {Number} scrollTop
35576          */
35577         "bodyscroll" : true,
35578         /**
35579          * @event columnresize
35580          * Fires when the user resizes a column
35581          * @param {Number} columnIndex
35582          * @param {Number} newSize
35583          */
35584         "columnresize" : true,
35585         /**
35586          * @event columnmove
35587          * Fires when the user moves a column
35588          * @param {Number} oldIndex
35589          * @param {Number} newIndex
35590          */
35591         "columnmove" : true,
35592         /**
35593          * @event startdrag
35594          * Fires when row(s) start being dragged
35595          * @param {Grid} this
35596          * @param {Roo.GridDD} dd The drag drop object
35597          * @param {event} e The raw browser event
35598          */
35599         "startdrag" : true,
35600         /**
35601          * @event enddrag
35602          * Fires when a drag operation is complete
35603          * @param {Grid} this
35604          * @param {Roo.GridDD} dd The drag drop object
35605          * @param {event} e The raw browser event
35606          */
35607         "enddrag" : true,
35608         /**
35609          * @event dragdrop
35610          * Fires when dragged row(s) are dropped on a valid DD target
35611          * @param {Grid} this
35612          * @param {Roo.GridDD} dd The drag drop object
35613          * @param {String} targetId The target drag drop object
35614          * @param {event} e The raw browser event
35615          */
35616         "dragdrop" : true,
35617         /**
35618          * @event dragover
35619          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35620          * @param {Grid} this
35621          * @param {Roo.GridDD} dd The drag drop object
35622          * @param {String} targetId The target drag drop object
35623          * @param {event} e The raw browser event
35624          */
35625         "dragover" : true,
35626         /**
35627          * @event dragenter
35628          *  Fires when the dragged row(s) first cross another DD target while being dragged
35629          * @param {Grid} this
35630          * @param {Roo.GridDD} dd The drag drop object
35631          * @param {String} targetId The target drag drop object
35632          * @param {event} e The raw browser event
35633          */
35634         "dragenter" : true,
35635         /**
35636          * @event dragout
35637          * Fires when the dragged row(s) leave another DD target while being dragged
35638          * @param {Grid} this
35639          * @param {Roo.GridDD} dd The drag drop object
35640          * @param {String} targetId The target drag drop object
35641          * @param {event} e The raw browser event
35642          */
35643         "dragout" : true,
35644         /**
35645          * @event rowclass
35646          * Fires when a row is rendered, so you can change add a style to it.
35647          * @param {GridView} gridview   The grid view
35648          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
35649          */
35650         'rowclass' : true,
35651
35652         /**
35653          * @event render
35654          * Fires when the grid is rendered
35655          * @param {Grid} grid
35656          */
35657         'render' : true
35658     });
35659
35660     Roo.grid.Grid.superclass.constructor.call(this);
35661 };
35662 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35663     
35664     /**
35665      * @cfg {String} ddGroup - drag drop group.
35666      */
35667
35668     /**
35669      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35670      */
35671     minColumnWidth : 25,
35672
35673     /**
35674      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35675      * <b>on initial render.</b> It is more efficient to explicitly size the columns
35676      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
35677      */
35678     autoSizeColumns : false,
35679
35680     /**
35681      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35682      */
35683     autoSizeHeaders : true,
35684
35685     /**
35686      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35687      */
35688     monitorWindowResize : true,
35689
35690     /**
35691      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35692      * rows measured to get a columns size. Default is 0 (all rows).
35693      */
35694     maxRowsToMeasure : 0,
35695
35696     /**
35697      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35698      */
35699     trackMouseOver : true,
35700
35701     /**
35702     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
35703     */
35704     
35705     /**
35706     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35707     */
35708     enableDragDrop : false,
35709     
35710     /**
35711     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35712     */
35713     enableColumnMove : true,
35714     
35715     /**
35716     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35717     */
35718     enableColumnHide : true,
35719     
35720     /**
35721     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35722     */
35723     enableRowHeightSync : false,
35724     
35725     /**
35726     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35727     */
35728     stripeRows : true,
35729     
35730     /**
35731     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35732     */
35733     autoHeight : false,
35734
35735     /**
35736      * @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.
35737      */
35738     autoExpandColumn : false,
35739
35740     /**
35741     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35742     * Default is 50.
35743     */
35744     autoExpandMin : 50,
35745
35746     /**
35747     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35748     */
35749     autoExpandMax : 1000,
35750
35751     /**
35752     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35753     */
35754     view : null,
35755
35756     /**
35757     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35758     */
35759     loadMask : false,
35760     /**
35761     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35762     */
35763     dropTarget: false,
35764     
35765    
35766     
35767     // private
35768     rendered : false,
35769
35770     /**
35771     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35772     * of a fixed width. Default is false.
35773     */
35774     /**
35775     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35776     */
35777     /**
35778      * Called once after all setup has been completed and the grid is ready to be rendered.
35779      * @return {Roo.grid.Grid} this
35780      */
35781     render : function()
35782     {
35783         var c = this.container;
35784         // try to detect autoHeight/width mode
35785         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35786             this.autoHeight = true;
35787         }
35788         var view = this.getView();
35789         view.init(this);
35790
35791         c.on("click", this.onClick, this);
35792         c.on("dblclick", this.onDblClick, this);
35793         c.on("contextmenu", this.onContextMenu, this);
35794         c.on("keydown", this.onKeyDown, this);
35795         if (Roo.isTouch) {
35796             c.on("touchstart", this.onTouchStart, this);
35797         }
35798
35799         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35800
35801         this.getSelectionModel().init(this);
35802
35803         view.render();
35804
35805         if(this.loadMask){
35806             this.loadMask = new Roo.LoadMask(this.container,
35807                     Roo.apply({store:this.dataSource}, this.loadMask));
35808         }
35809         
35810         
35811         if (this.toolbar && this.toolbar.xtype) {
35812             this.toolbar.container = this.getView().getHeaderPanel(true);
35813             this.toolbar = new Roo.Toolbar(this.toolbar);
35814         }
35815         if (this.footer && this.footer.xtype) {
35816             this.footer.dataSource = this.getDataSource();
35817             this.footer.container = this.getView().getFooterPanel(true);
35818             this.footer = Roo.factory(this.footer, Roo);
35819         }
35820         if (this.dropTarget && this.dropTarget.xtype) {
35821             delete this.dropTarget.xtype;
35822             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35823         }
35824         
35825         
35826         this.rendered = true;
35827         this.fireEvent('render', this);
35828         return this;
35829     },
35830
35831         /**
35832          * Reconfigures the grid to use a different Store and Column Model.
35833          * The View will be bound to the new objects and refreshed.
35834          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35835          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35836          */
35837     reconfigure : function(dataSource, colModel){
35838         if(this.loadMask){
35839             this.loadMask.destroy();
35840             this.loadMask = new Roo.LoadMask(this.container,
35841                     Roo.apply({store:dataSource}, this.loadMask));
35842         }
35843         this.view.bind(dataSource, colModel);
35844         this.dataSource = dataSource;
35845         this.colModel = colModel;
35846         this.view.refresh(true);
35847     },
35848
35849     // private
35850     onKeyDown : function(e){
35851         this.fireEvent("keydown", e);
35852     },
35853
35854     /**
35855      * Destroy this grid.
35856      * @param {Boolean} removeEl True to remove the element
35857      */
35858     destroy : function(removeEl, keepListeners){
35859         if(this.loadMask){
35860             this.loadMask.destroy();
35861         }
35862         var c = this.container;
35863         c.removeAllListeners();
35864         this.view.destroy();
35865         this.colModel.purgeListeners();
35866         if(!keepListeners){
35867             this.purgeListeners();
35868         }
35869         c.update("");
35870         if(removeEl === true){
35871             c.remove();
35872         }
35873     },
35874
35875     // private
35876     processEvent : function(name, e){
35877         // does this fire select???
35878         Roo.log('grid:processEvent '  + name);
35879         
35880         if (name != 'touchstart' ) {
35881             this.fireEvent(name, e);    
35882         }
35883         
35884         var t = e.getTarget();
35885         var v = this.view;
35886         var header = v.findHeaderIndex(t);
35887         if(header !== false){
35888             this.fireEvent("header" + (name == 'touchstart' ? 'click' : name), this, header, e);
35889         }else{
35890             var row = v.findRowIndex(t);
35891             var cell = v.findCellIndex(t);
35892             if (name == 'touchstart') {
35893                 // first touch is always a click.
35894                 // hopefull this happens after selection is updated.?
35895                 name = false;
35896                 
35897                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
35898                     var cs = this.selModel.getSelectedCell();
35899                     if (row == cs[0] && cell == cs[1]){
35900                         name = 'dblclick';
35901                     }
35902                 }
35903                 if (typeof(this.selModel.getSelections) != 'undefined') {
35904                     var cs = this.selModel.getSelections();
35905                     var ds = this.dataSource;
35906                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
35907                         name = 'dblclick';
35908                     }
35909                 }
35910                 if (!name) {
35911                     return;
35912                 }
35913             }
35914             
35915             
35916             if(row !== false){
35917                 this.fireEvent("row" + name, this, row, e);
35918                 if(cell !== false){
35919                     this.fireEvent("cell" + name, this, row, cell, e);
35920                 }
35921             }
35922         }
35923     },
35924
35925     // private
35926     onClick : function(e){
35927         this.processEvent("click", e);
35928     },
35929    // private
35930     onTouchStart : function(e){
35931         this.processEvent("touchstart", e);
35932     },
35933
35934     // private
35935     onContextMenu : function(e, t){
35936         this.processEvent("contextmenu", e);
35937     },
35938
35939     // private
35940     onDblClick : function(e){
35941         this.processEvent("dblclick", e);
35942     },
35943
35944     // private
35945     walkCells : function(row, col, step, fn, scope){
35946         var cm = this.colModel, clen = cm.getColumnCount();
35947         var ds = this.dataSource, rlen = ds.getCount(), first = true;
35948         if(step < 0){
35949             if(col < 0){
35950                 row--;
35951                 first = false;
35952             }
35953             while(row >= 0){
35954                 if(!first){
35955                     col = clen-1;
35956                 }
35957                 first = false;
35958                 while(col >= 0){
35959                     if(fn.call(scope || this, row, col, cm) === true){
35960                         return [row, col];
35961                     }
35962                     col--;
35963                 }
35964                 row--;
35965             }
35966         } else {
35967             if(col >= clen){
35968                 row++;
35969                 first = false;
35970             }
35971             while(row < rlen){
35972                 if(!first){
35973                     col = 0;
35974                 }
35975                 first = false;
35976                 while(col < clen){
35977                     if(fn.call(scope || this, row, col, cm) === true){
35978                         return [row, col];
35979                     }
35980                     col++;
35981                 }
35982                 row++;
35983             }
35984         }
35985         return null;
35986     },
35987
35988     // private
35989     getSelections : function(){
35990         return this.selModel.getSelections();
35991     },
35992
35993     /**
35994      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
35995      * but if manual update is required this method will initiate it.
35996      */
35997     autoSize : function(){
35998         if(this.rendered){
35999             this.view.layout();
36000             if(this.view.adjustForScroll){
36001                 this.view.adjustForScroll();
36002             }
36003         }
36004     },
36005
36006     /**
36007      * Returns the grid's underlying element.
36008      * @return {Element} The element
36009      */
36010     getGridEl : function(){
36011         return this.container;
36012     },
36013
36014     // private for compatibility, overridden by editor grid
36015     stopEditing : function(){},
36016
36017     /**
36018      * Returns the grid's SelectionModel.
36019      * @return {SelectionModel}
36020      */
36021     getSelectionModel : function(){
36022         if(!this.selModel){
36023             this.selModel = new Roo.grid.RowSelectionModel();
36024         }
36025         return this.selModel;
36026     },
36027
36028     /**
36029      * Returns the grid's DataSource.
36030      * @return {DataSource}
36031      */
36032     getDataSource : function(){
36033         return this.dataSource;
36034     },
36035
36036     /**
36037      * Returns the grid's ColumnModel.
36038      * @return {ColumnModel}
36039      */
36040     getColumnModel : function(){
36041         return this.colModel;
36042     },
36043
36044     /**
36045      * Returns the grid's GridView object.
36046      * @return {GridView}
36047      */
36048     getView : function(){
36049         if(!this.view){
36050             this.view = new Roo.grid.GridView(this.viewConfig);
36051         }
36052         return this.view;
36053     },
36054     /**
36055      * Called to get grid's drag proxy text, by default returns this.ddText.
36056      * @return {String}
36057      */
36058     getDragDropText : function(){
36059         var count = this.selModel.getCount();
36060         return String.format(this.ddText, count, count == 1 ? '' : 's');
36061     }
36062 });
36063 /**
36064  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36065  * %0 is replaced with the number of selected rows.
36066  * @type String
36067  */
36068 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36069  * Based on:
36070  * Ext JS Library 1.1.1
36071  * Copyright(c) 2006-2007, Ext JS, LLC.
36072  *
36073  * Originally Released Under LGPL - original licence link has changed is not relivant.
36074  *
36075  * Fork - LGPL
36076  * <script type="text/javascript">
36077  */
36078  
36079 Roo.grid.AbstractGridView = function(){
36080         this.grid = null;
36081         
36082         this.events = {
36083             "beforerowremoved" : true,
36084             "beforerowsinserted" : true,
36085             "beforerefresh" : true,
36086             "rowremoved" : true,
36087             "rowsinserted" : true,
36088             "rowupdated" : true,
36089             "refresh" : true
36090         };
36091     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36092 };
36093
36094 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36095     rowClass : "x-grid-row",
36096     cellClass : "x-grid-cell",
36097     tdClass : "x-grid-td",
36098     hdClass : "x-grid-hd",
36099     splitClass : "x-grid-hd-split",
36100     
36101         init: function(grid){
36102         this.grid = grid;
36103                 var cid = this.grid.getGridEl().id;
36104         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36105         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36106         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36107         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36108         },
36109         
36110         getColumnRenderers : function(){
36111         var renderers = [];
36112         var cm = this.grid.colModel;
36113         var colCount = cm.getColumnCount();
36114         for(var i = 0; i < colCount; i++){
36115             renderers[i] = cm.getRenderer(i);
36116         }
36117         return renderers;
36118     },
36119     
36120     getColumnIds : function(){
36121         var ids = [];
36122         var cm = this.grid.colModel;
36123         var colCount = cm.getColumnCount();
36124         for(var i = 0; i < colCount; i++){
36125             ids[i] = cm.getColumnId(i);
36126         }
36127         return ids;
36128     },
36129     
36130     getDataIndexes : function(){
36131         if(!this.indexMap){
36132             this.indexMap = this.buildIndexMap();
36133         }
36134         return this.indexMap.colToData;
36135     },
36136     
36137     getColumnIndexByDataIndex : function(dataIndex){
36138         if(!this.indexMap){
36139             this.indexMap = this.buildIndexMap();
36140         }
36141         return this.indexMap.dataToCol[dataIndex];
36142     },
36143     
36144     /**
36145      * Set a css style for a column dynamically. 
36146      * @param {Number} colIndex The index of the column
36147      * @param {String} name The css property name
36148      * @param {String} value The css value
36149      */
36150     setCSSStyle : function(colIndex, name, value){
36151         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36152         Roo.util.CSS.updateRule(selector, name, value);
36153     },
36154     
36155     generateRules : function(cm){
36156         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36157         Roo.util.CSS.removeStyleSheet(rulesId);
36158         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36159             var cid = cm.getColumnId(i);
36160             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36161                          this.tdSelector, cid, " {\n}\n",
36162                          this.hdSelector, cid, " {\n}\n",
36163                          this.splitSelector, cid, " {\n}\n");
36164         }
36165         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36166     }
36167 });/*
36168  * Based on:
36169  * Ext JS Library 1.1.1
36170  * Copyright(c) 2006-2007, Ext JS, LLC.
36171  *
36172  * Originally Released Under LGPL - original licence link has changed is not relivant.
36173  *
36174  * Fork - LGPL
36175  * <script type="text/javascript">
36176  */
36177
36178 // private
36179 // This is a support class used internally by the Grid components
36180 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36181     this.grid = grid;
36182     this.view = grid.getView();
36183     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36184     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36185     if(hd2){
36186         this.setHandleElId(Roo.id(hd));
36187         this.setOuterHandleElId(Roo.id(hd2));
36188     }
36189     this.scroll = false;
36190 };
36191 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36192     maxDragWidth: 120,
36193     getDragData : function(e){
36194         var t = Roo.lib.Event.getTarget(e);
36195         var h = this.view.findHeaderCell(t);
36196         if(h){
36197             return {ddel: h.firstChild, header:h};
36198         }
36199         return false;
36200     },
36201
36202     onInitDrag : function(e){
36203         this.view.headersDisabled = true;
36204         var clone = this.dragData.ddel.cloneNode(true);
36205         clone.id = Roo.id();
36206         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36207         this.proxy.update(clone);
36208         return true;
36209     },
36210
36211     afterValidDrop : function(){
36212         var v = this.view;
36213         setTimeout(function(){
36214             v.headersDisabled = false;
36215         }, 50);
36216     },
36217
36218     afterInvalidDrop : function(){
36219         var v = this.view;
36220         setTimeout(function(){
36221             v.headersDisabled = false;
36222         }, 50);
36223     }
36224 });
36225 /*
36226  * Based on:
36227  * Ext JS Library 1.1.1
36228  * Copyright(c) 2006-2007, Ext JS, LLC.
36229  *
36230  * Originally Released Under LGPL - original licence link has changed is not relivant.
36231  *
36232  * Fork - LGPL
36233  * <script type="text/javascript">
36234  */
36235 // private
36236 // This is a support class used internally by the Grid components
36237 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36238     this.grid = grid;
36239     this.view = grid.getView();
36240     // split the proxies so they don't interfere with mouse events
36241     this.proxyTop = Roo.DomHelper.append(document.body, {
36242         cls:"col-move-top", html:"&#160;"
36243     }, true);
36244     this.proxyBottom = Roo.DomHelper.append(document.body, {
36245         cls:"col-move-bottom", html:"&#160;"
36246     }, true);
36247     this.proxyTop.hide = this.proxyBottom.hide = function(){
36248         this.setLeftTop(-100,-100);
36249         this.setStyle("visibility", "hidden");
36250     };
36251     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36252     // temporarily disabled
36253     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36254     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36255 };
36256 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36257     proxyOffsets : [-4, -9],
36258     fly: Roo.Element.fly,
36259
36260     getTargetFromEvent : function(e){
36261         var t = Roo.lib.Event.getTarget(e);
36262         var cindex = this.view.findCellIndex(t);
36263         if(cindex !== false){
36264             return this.view.getHeaderCell(cindex);
36265         }
36266         return null;
36267     },
36268
36269     nextVisible : function(h){
36270         var v = this.view, cm = this.grid.colModel;
36271         h = h.nextSibling;
36272         while(h){
36273             if(!cm.isHidden(v.getCellIndex(h))){
36274                 return h;
36275             }
36276             h = h.nextSibling;
36277         }
36278         return null;
36279     },
36280
36281     prevVisible : function(h){
36282         var v = this.view, cm = this.grid.colModel;
36283         h = h.prevSibling;
36284         while(h){
36285             if(!cm.isHidden(v.getCellIndex(h))){
36286                 return h;
36287             }
36288             h = h.prevSibling;
36289         }
36290         return null;
36291     },
36292
36293     positionIndicator : function(h, n, e){
36294         var x = Roo.lib.Event.getPageX(e);
36295         var r = Roo.lib.Dom.getRegion(n.firstChild);
36296         var px, pt, py = r.top + this.proxyOffsets[1];
36297         if((r.right - x) <= (r.right-r.left)/2){
36298             px = r.right+this.view.borderWidth;
36299             pt = "after";
36300         }else{
36301             px = r.left;
36302             pt = "before";
36303         }
36304         var oldIndex = this.view.getCellIndex(h);
36305         var newIndex = this.view.getCellIndex(n);
36306
36307         if(this.grid.colModel.isFixed(newIndex)){
36308             return false;
36309         }
36310
36311         var locked = this.grid.colModel.isLocked(newIndex);
36312
36313         if(pt == "after"){
36314             newIndex++;
36315         }
36316         if(oldIndex < newIndex){
36317             newIndex--;
36318         }
36319         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36320             return false;
36321         }
36322         px +=  this.proxyOffsets[0];
36323         this.proxyTop.setLeftTop(px, py);
36324         this.proxyTop.show();
36325         if(!this.bottomOffset){
36326             this.bottomOffset = this.view.mainHd.getHeight();
36327         }
36328         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36329         this.proxyBottom.show();
36330         return pt;
36331     },
36332
36333     onNodeEnter : function(n, dd, e, data){
36334         if(data.header != n){
36335             this.positionIndicator(data.header, n, e);
36336         }
36337     },
36338
36339     onNodeOver : function(n, dd, e, data){
36340         var result = false;
36341         if(data.header != n){
36342             result = this.positionIndicator(data.header, n, e);
36343         }
36344         if(!result){
36345             this.proxyTop.hide();
36346             this.proxyBottom.hide();
36347         }
36348         return result ? this.dropAllowed : this.dropNotAllowed;
36349     },
36350
36351     onNodeOut : function(n, dd, e, data){
36352         this.proxyTop.hide();
36353         this.proxyBottom.hide();
36354     },
36355
36356     onNodeDrop : function(n, dd, e, data){
36357         var h = data.header;
36358         if(h != n){
36359             var cm = this.grid.colModel;
36360             var x = Roo.lib.Event.getPageX(e);
36361             var r = Roo.lib.Dom.getRegion(n.firstChild);
36362             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36363             var oldIndex = this.view.getCellIndex(h);
36364             var newIndex = this.view.getCellIndex(n);
36365             var locked = cm.isLocked(newIndex);
36366             if(pt == "after"){
36367                 newIndex++;
36368             }
36369             if(oldIndex < newIndex){
36370                 newIndex--;
36371             }
36372             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36373                 return false;
36374             }
36375             cm.setLocked(oldIndex, locked, true);
36376             cm.moveColumn(oldIndex, newIndex);
36377             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36378             return true;
36379         }
36380         return false;
36381     }
36382 });
36383 /*
36384  * Based on:
36385  * Ext JS Library 1.1.1
36386  * Copyright(c) 2006-2007, Ext JS, LLC.
36387  *
36388  * Originally Released Under LGPL - original licence link has changed is not relivant.
36389  *
36390  * Fork - LGPL
36391  * <script type="text/javascript">
36392  */
36393   
36394 /**
36395  * @class Roo.grid.GridView
36396  * @extends Roo.util.Observable
36397  *
36398  * @constructor
36399  * @param {Object} config
36400  */
36401 Roo.grid.GridView = function(config){
36402     Roo.grid.GridView.superclass.constructor.call(this);
36403     this.el = null;
36404
36405     Roo.apply(this, config);
36406 };
36407
36408 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36409
36410     unselectable :  'unselectable="on"',
36411     unselectableCls :  'x-unselectable',
36412     
36413     
36414     rowClass : "x-grid-row",
36415
36416     cellClass : "x-grid-col",
36417
36418     tdClass : "x-grid-td",
36419
36420     hdClass : "x-grid-hd",
36421
36422     splitClass : "x-grid-split",
36423
36424     sortClasses : ["sort-asc", "sort-desc"],
36425
36426     enableMoveAnim : false,
36427
36428     hlColor: "C3DAF9",
36429
36430     dh : Roo.DomHelper,
36431
36432     fly : Roo.Element.fly,
36433
36434     css : Roo.util.CSS,
36435
36436     borderWidth: 1,
36437
36438     splitOffset: 3,
36439
36440     scrollIncrement : 22,
36441
36442     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36443
36444     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36445
36446     bind : function(ds, cm){
36447         if(this.ds){
36448             this.ds.un("load", this.onLoad, this);
36449             this.ds.un("datachanged", this.onDataChange, this);
36450             this.ds.un("add", this.onAdd, this);
36451             this.ds.un("remove", this.onRemove, this);
36452             this.ds.un("update", this.onUpdate, this);
36453             this.ds.un("clear", this.onClear, this);
36454         }
36455         if(ds){
36456             ds.on("load", this.onLoad, this);
36457             ds.on("datachanged", this.onDataChange, this);
36458             ds.on("add", this.onAdd, this);
36459             ds.on("remove", this.onRemove, this);
36460             ds.on("update", this.onUpdate, this);
36461             ds.on("clear", this.onClear, this);
36462         }
36463         this.ds = ds;
36464
36465         if(this.cm){
36466             this.cm.un("widthchange", this.onColWidthChange, this);
36467             this.cm.un("headerchange", this.onHeaderChange, this);
36468             this.cm.un("hiddenchange", this.onHiddenChange, this);
36469             this.cm.un("columnmoved", this.onColumnMove, this);
36470             this.cm.un("columnlockchange", this.onColumnLock, this);
36471         }
36472         if(cm){
36473             this.generateRules(cm);
36474             cm.on("widthchange", this.onColWidthChange, this);
36475             cm.on("headerchange", this.onHeaderChange, this);
36476             cm.on("hiddenchange", this.onHiddenChange, this);
36477             cm.on("columnmoved", this.onColumnMove, this);
36478             cm.on("columnlockchange", this.onColumnLock, this);
36479         }
36480         this.cm = cm;
36481     },
36482
36483     init: function(grid){
36484         Roo.grid.GridView.superclass.init.call(this, grid);
36485
36486         this.bind(grid.dataSource, grid.colModel);
36487
36488         grid.on("headerclick", this.handleHeaderClick, this);
36489
36490         if(grid.trackMouseOver){
36491             grid.on("mouseover", this.onRowOver, this);
36492             grid.on("mouseout", this.onRowOut, this);
36493         }
36494         grid.cancelTextSelection = function(){};
36495         this.gridId = grid.id;
36496
36497         var tpls = this.templates || {};
36498
36499         if(!tpls.master){
36500             tpls.master = new Roo.Template(
36501                '<div class="x-grid" hidefocus="true">',
36502                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36503                   '<div class="x-grid-topbar"></div>',
36504                   '<div class="x-grid-scroller"><div></div></div>',
36505                   '<div class="x-grid-locked">',
36506                       '<div class="x-grid-header">{lockedHeader}</div>',
36507                       '<div class="x-grid-body">{lockedBody}</div>',
36508                   "</div>",
36509                   '<div class="x-grid-viewport">',
36510                       '<div class="x-grid-header">{header}</div>',
36511                       '<div class="x-grid-body">{body}</div>',
36512                   "</div>",
36513                   '<div class="x-grid-bottombar"></div>',
36514                  
36515                   '<div class="x-grid-resize-proxy">&#160;</div>',
36516                "</div>"
36517             );
36518             tpls.master.disableformats = true;
36519         }
36520
36521         if(!tpls.header){
36522             tpls.header = new Roo.Template(
36523                '<table border="0" cellspacing="0" cellpadding="0">',
36524                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36525                "</table>{splits}"
36526             );
36527             tpls.header.disableformats = true;
36528         }
36529         tpls.header.compile();
36530
36531         if(!tpls.hcell){
36532             tpls.hcell = new Roo.Template(
36533                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36534                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36535                 "</div></td>"
36536              );
36537              tpls.hcell.disableFormats = true;
36538         }
36539         tpls.hcell.compile();
36540
36541         if(!tpls.hsplit){
36542             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36543                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
36544             tpls.hsplit.disableFormats = true;
36545         }
36546         tpls.hsplit.compile();
36547
36548         if(!tpls.body){
36549             tpls.body = new Roo.Template(
36550                '<table border="0" cellspacing="0" cellpadding="0">',
36551                "<tbody>{rows}</tbody>",
36552                "</table>"
36553             );
36554             tpls.body.disableFormats = true;
36555         }
36556         tpls.body.compile();
36557
36558         if(!tpls.row){
36559             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36560             tpls.row.disableFormats = true;
36561         }
36562         tpls.row.compile();
36563
36564         if(!tpls.cell){
36565             tpls.cell = new Roo.Template(
36566                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36567                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36568                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36569                 "</td>"
36570             );
36571             tpls.cell.disableFormats = true;
36572         }
36573         tpls.cell.compile();
36574
36575         this.templates = tpls;
36576     },
36577
36578     // remap these for backwards compat
36579     onColWidthChange : function(){
36580         this.updateColumns.apply(this, arguments);
36581     },
36582     onHeaderChange : function(){
36583         this.updateHeaders.apply(this, arguments);
36584     }, 
36585     onHiddenChange : function(){
36586         this.handleHiddenChange.apply(this, arguments);
36587     },
36588     onColumnMove : function(){
36589         this.handleColumnMove.apply(this, arguments);
36590     },
36591     onColumnLock : function(){
36592         this.handleLockChange.apply(this, arguments);
36593     },
36594
36595     onDataChange : function(){
36596         this.refresh();
36597         this.updateHeaderSortState();
36598     },
36599
36600     onClear : function(){
36601         this.refresh();
36602     },
36603
36604     onUpdate : function(ds, record){
36605         this.refreshRow(record);
36606     },
36607
36608     refreshRow : function(record){
36609         var ds = this.ds, index;
36610         if(typeof record == 'number'){
36611             index = record;
36612             record = ds.getAt(index);
36613         }else{
36614             index = ds.indexOf(record);
36615         }
36616         this.insertRows(ds, index, index, true);
36617         this.onRemove(ds, record, index+1, true);
36618         this.syncRowHeights(index, index);
36619         this.layout();
36620         this.fireEvent("rowupdated", this, index, record);
36621     },
36622
36623     onAdd : function(ds, records, index){
36624         this.insertRows(ds, index, index + (records.length-1));
36625     },
36626
36627     onRemove : function(ds, record, index, isUpdate){
36628         if(isUpdate !== true){
36629             this.fireEvent("beforerowremoved", this, index, record);
36630         }
36631         var bt = this.getBodyTable(), lt = this.getLockedTable();
36632         if(bt.rows[index]){
36633             bt.firstChild.removeChild(bt.rows[index]);
36634         }
36635         if(lt.rows[index]){
36636             lt.firstChild.removeChild(lt.rows[index]);
36637         }
36638         if(isUpdate !== true){
36639             this.stripeRows(index);
36640             this.syncRowHeights(index, index);
36641             this.layout();
36642             this.fireEvent("rowremoved", this, index, record);
36643         }
36644     },
36645
36646     onLoad : function(){
36647         this.scrollToTop();
36648     },
36649
36650     /**
36651      * Scrolls the grid to the top
36652      */
36653     scrollToTop : function(){
36654         if(this.scroller){
36655             this.scroller.dom.scrollTop = 0;
36656             this.syncScroll();
36657         }
36658     },
36659
36660     /**
36661      * Gets a panel in the header of the grid that can be used for toolbars etc.
36662      * After modifying the contents of this panel a call to grid.autoSize() may be
36663      * required to register any changes in size.
36664      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36665      * @return Roo.Element
36666      */
36667     getHeaderPanel : function(doShow){
36668         if(doShow){
36669             this.headerPanel.show();
36670         }
36671         return this.headerPanel;
36672     },
36673
36674     /**
36675      * Gets a panel in the footer of the grid that can be used for toolbars etc.
36676      * After modifying the contents of this panel a call to grid.autoSize() may be
36677      * required to register any changes in size.
36678      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36679      * @return Roo.Element
36680      */
36681     getFooterPanel : function(doShow){
36682         if(doShow){
36683             this.footerPanel.show();
36684         }
36685         return this.footerPanel;
36686     },
36687
36688     initElements : function(){
36689         var E = Roo.Element;
36690         var el = this.grid.getGridEl().dom.firstChild;
36691         var cs = el.childNodes;
36692
36693         this.el = new E(el);
36694         
36695          this.focusEl = new E(el.firstChild);
36696         this.focusEl.swallowEvent("click", true);
36697         
36698         this.headerPanel = new E(cs[1]);
36699         this.headerPanel.enableDisplayMode("block");
36700
36701         this.scroller = new E(cs[2]);
36702         this.scrollSizer = new E(this.scroller.dom.firstChild);
36703
36704         this.lockedWrap = new E(cs[3]);
36705         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36706         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36707
36708         this.mainWrap = new E(cs[4]);
36709         this.mainHd = new E(this.mainWrap.dom.firstChild);
36710         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36711
36712         this.footerPanel = new E(cs[5]);
36713         this.footerPanel.enableDisplayMode("block");
36714
36715         this.resizeProxy = new E(cs[6]);
36716
36717         this.headerSelector = String.format(
36718            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36719            this.lockedHd.id, this.mainHd.id
36720         );
36721
36722         this.splitterSelector = String.format(
36723            '#{0} div.x-grid-split, #{1} div.x-grid-split',
36724            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36725         );
36726     },
36727     idToCssName : function(s)
36728     {
36729         return s.replace(/[^a-z0-9]+/ig, '-');
36730     },
36731
36732     getHeaderCell : function(index){
36733         return Roo.DomQuery.select(this.headerSelector)[index];
36734     },
36735
36736     getHeaderCellMeasure : function(index){
36737         return this.getHeaderCell(index).firstChild;
36738     },
36739
36740     getHeaderCellText : function(index){
36741         return this.getHeaderCell(index).firstChild.firstChild;
36742     },
36743
36744     getLockedTable : function(){
36745         return this.lockedBody.dom.firstChild;
36746     },
36747
36748     getBodyTable : function(){
36749         return this.mainBody.dom.firstChild;
36750     },
36751
36752     getLockedRow : function(index){
36753         return this.getLockedTable().rows[index];
36754     },
36755
36756     getRow : function(index){
36757         return this.getBodyTable().rows[index];
36758     },
36759
36760     getRowComposite : function(index){
36761         if(!this.rowEl){
36762             this.rowEl = new Roo.CompositeElementLite();
36763         }
36764         var els = [], lrow, mrow;
36765         if(lrow = this.getLockedRow(index)){
36766             els.push(lrow);
36767         }
36768         if(mrow = this.getRow(index)){
36769             els.push(mrow);
36770         }
36771         this.rowEl.elements = els;
36772         return this.rowEl;
36773     },
36774     /**
36775      * Gets the 'td' of the cell
36776      * 
36777      * @param {Integer} rowIndex row to select
36778      * @param {Integer} colIndex column to select
36779      * 
36780      * @return {Object} 
36781      */
36782     getCell : function(rowIndex, colIndex){
36783         var locked = this.cm.getLockedCount();
36784         var source;
36785         if(colIndex < locked){
36786             source = this.lockedBody.dom.firstChild;
36787         }else{
36788             source = this.mainBody.dom.firstChild;
36789             colIndex -= locked;
36790         }
36791         return source.rows[rowIndex].childNodes[colIndex];
36792     },
36793
36794     getCellText : function(rowIndex, colIndex){
36795         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36796     },
36797
36798     getCellBox : function(cell){
36799         var b = this.fly(cell).getBox();
36800         if(Roo.isOpera){ // opera fails to report the Y
36801             b.y = cell.offsetTop + this.mainBody.getY();
36802         }
36803         return b;
36804     },
36805
36806     getCellIndex : function(cell){
36807         var id = String(cell.className).match(this.cellRE);
36808         if(id){
36809             return parseInt(id[1], 10);
36810         }
36811         return 0;
36812     },
36813
36814     findHeaderIndex : function(n){
36815         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36816         return r ? this.getCellIndex(r) : false;
36817     },
36818
36819     findHeaderCell : function(n){
36820         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36821         return r ? r : false;
36822     },
36823
36824     findRowIndex : function(n){
36825         if(!n){
36826             return false;
36827         }
36828         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36829         return r ? r.rowIndex : false;
36830     },
36831
36832     findCellIndex : function(node){
36833         var stop = this.el.dom;
36834         while(node && node != stop){
36835             if(this.findRE.test(node.className)){
36836                 return this.getCellIndex(node);
36837             }
36838             node = node.parentNode;
36839         }
36840         return false;
36841     },
36842
36843     getColumnId : function(index){
36844         return this.cm.getColumnId(index);
36845     },
36846
36847     getSplitters : function()
36848     {
36849         if(this.splitterSelector){
36850            return Roo.DomQuery.select(this.splitterSelector);
36851         }else{
36852             return null;
36853       }
36854     },
36855
36856     getSplitter : function(index){
36857         return this.getSplitters()[index];
36858     },
36859
36860     onRowOver : function(e, t){
36861         var row;
36862         if((row = this.findRowIndex(t)) !== false){
36863             this.getRowComposite(row).addClass("x-grid-row-over");
36864         }
36865     },
36866
36867     onRowOut : function(e, t){
36868         var row;
36869         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
36870             this.getRowComposite(row).removeClass("x-grid-row-over");
36871         }
36872     },
36873
36874     renderHeaders : function(){
36875         var cm = this.cm;
36876         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
36877         var cb = [], lb = [], sb = [], lsb = [], p = {};
36878         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36879             p.cellId = "x-grid-hd-0-" + i;
36880             p.splitId = "x-grid-csplit-0-" + i;
36881             p.id = cm.getColumnId(i);
36882             p.title = cm.getColumnTooltip(i) || "";
36883             p.value = cm.getColumnHeader(i) || "";
36884             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
36885             if(!cm.isLocked(i)){
36886                 cb[cb.length] = ct.apply(p);
36887                 sb[sb.length] = st.apply(p);
36888             }else{
36889                 lb[lb.length] = ct.apply(p);
36890                 lsb[lsb.length] = st.apply(p);
36891             }
36892         }
36893         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
36894                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
36895     },
36896
36897     updateHeaders : function(){
36898         var html = this.renderHeaders();
36899         this.lockedHd.update(html[0]);
36900         this.mainHd.update(html[1]);
36901     },
36902
36903     /**
36904      * Focuses the specified row.
36905      * @param {Number} row The row index
36906      */
36907     focusRow : function(row)
36908     {
36909         //Roo.log('GridView.focusRow');
36910         var x = this.scroller.dom.scrollLeft;
36911         this.focusCell(row, 0, false);
36912         this.scroller.dom.scrollLeft = x;
36913     },
36914
36915     /**
36916      * Focuses the specified cell.
36917      * @param {Number} row The row index
36918      * @param {Number} col The column index
36919      * @param {Boolean} hscroll false to disable horizontal scrolling
36920      */
36921     focusCell : function(row, col, hscroll)
36922     {
36923         //Roo.log('GridView.focusCell');
36924         var el = this.ensureVisible(row, col, hscroll);
36925         this.focusEl.alignTo(el, "tl-tl");
36926         if(Roo.isGecko){
36927             this.focusEl.focus();
36928         }else{
36929             this.focusEl.focus.defer(1, this.focusEl);
36930         }
36931     },
36932
36933     /**
36934      * Scrolls the specified cell into view
36935      * @param {Number} row The row index
36936      * @param {Number} col The column index
36937      * @param {Boolean} hscroll false to disable horizontal scrolling
36938      */
36939     ensureVisible : function(row, col, hscroll)
36940     {
36941         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
36942         //return null; //disable for testing.
36943         if(typeof row != "number"){
36944             row = row.rowIndex;
36945         }
36946         if(row < 0 && row >= this.ds.getCount()){
36947             return  null;
36948         }
36949         col = (col !== undefined ? col : 0);
36950         var cm = this.grid.colModel;
36951         while(cm.isHidden(col)){
36952             col++;
36953         }
36954
36955         var el = this.getCell(row, col);
36956         if(!el){
36957             return null;
36958         }
36959         var c = this.scroller.dom;
36960
36961         var ctop = parseInt(el.offsetTop, 10);
36962         var cleft = parseInt(el.offsetLeft, 10);
36963         var cbot = ctop + el.offsetHeight;
36964         var cright = cleft + el.offsetWidth;
36965         
36966         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
36967         var stop = parseInt(c.scrollTop, 10);
36968         var sleft = parseInt(c.scrollLeft, 10);
36969         var sbot = stop + ch;
36970         var sright = sleft + c.clientWidth;
36971         /*
36972         Roo.log('GridView.ensureVisible:' +
36973                 ' ctop:' + ctop +
36974                 ' c.clientHeight:' + c.clientHeight +
36975                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
36976                 ' stop:' + stop +
36977                 ' cbot:' + cbot +
36978                 ' sbot:' + sbot +
36979                 ' ch:' + ch  
36980                 );
36981         */
36982         if(ctop < stop){
36983              c.scrollTop = ctop;
36984             //Roo.log("set scrolltop to ctop DISABLE?");
36985         }else if(cbot > sbot){
36986             //Roo.log("set scrolltop to cbot-ch");
36987             c.scrollTop = cbot-ch;
36988         }
36989         
36990         if(hscroll !== false){
36991             if(cleft < sleft){
36992                 c.scrollLeft = cleft;
36993             }else if(cright > sright){
36994                 c.scrollLeft = cright-c.clientWidth;
36995             }
36996         }
36997          
36998         return el;
36999     },
37000
37001     updateColumns : function(){
37002         this.grid.stopEditing();
37003         var cm = this.grid.colModel, colIds = this.getColumnIds();
37004         //var totalWidth = cm.getTotalWidth();
37005         var pos = 0;
37006         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37007             //if(cm.isHidden(i)) continue;
37008             var w = cm.getColumnWidth(i);
37009             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37010             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37011         }
37012         this.updateSplitters();
37013     },
37014
37015     generateRules : function(cm){
37016         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37017         Roo.util.CSS.removeStyleSheet(rulesId);
37018         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37019             var cid = cm.getColumnId(i);
37020             var align = '';
37021             if(cm.config[i].align){
37022                 align = 'text-align:'+cm.config[i].align+';';
37023             }
37024             var hidden = '';
37025             if(cm.isHidden(i)){
37026                 hidden = 'display:none;';
37027             }
37028             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37029             ruleBuf.push(
37030                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37031                     this.hdSelector, cid, " {\n", align, width, "}\n",
37032                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37033                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37034         }
37035         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37036     },
37037
37038     updateSplitters : function(){
37039         var cm = this.cm, s = this.getSplitters();
37040         if(s){ // splitters not created yet
37041             var pos = 0, locked = true;
37042             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37043                 if(cm.isHidden(i)) continue;
37044                 var w = cm.getColumnWidth(i); // make sure it's a number
37045                 if(!cm.isLocked(i) && locked){
37046                     pos = 0;
37047                     locked = false;
37048                 }
37049                 pos += w;
37050                 s[i].style.left = (pos-this.splitOffset) + "px";
37051             }
37052         }
37053     },
37054
37055     handleHiddenChange : function(colModel, colIndex, hidden){
37056         if(hidden){
37057             this.hideColumn(colIndex);
37058         }else{
37059             this.unhideColumn(colIndex);
37060         }
37061     },
37062
37063     hideColumn : function(colIndex){
37064         var cid = this.getColumnId(colIndex);
37065         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37066         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37067         if(Roo.isSafari){
37068             this.updateHeaders();
37069         }
37070         this.updateSplitters();
37071         this.layout();
37072     },
37073
37074     unhideColumn : function(colIndex){
37075         var cid = this.getColumnId(colIndex);
37076         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37077         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37078
37079         if(Roo.isSafari){
37080             this.updateHeaders();
37081         }
37082         this.updateSplitters();
37083         this.layout();
37084     },
37085
37086     insertRows : function(dm, firstRow, lastRow, isUpdate){
37087         if(firstRow == 0 && lastRow == dm.getCount()-1){
37088             this.refresh();
37089         }else{
37090             if(!isUpdate){
37091                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37092             }
37093             var s = this.getScrollState();
37094             var markup = this.renderRows(firstRow, lastRow);
37095             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37096             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37097             this.restoreScroll(s);
37098             if(!isUpdate){
37099                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37100                 this.syncRowHeights(firstRow, lastRow);
37101                 this.stripeRows(firstRow);
37102                 this.layout();
37103             }
37104         }
37105     },
37106
37107     bufferRows : function(markup, target, index){
37108         var before = null, trows = target.rows, tbody = target.tBodies[0];
37109         if(index < trows.length){
37110             before = trows[index];
37111         }
37112         var b = document.createElement("div");
37113         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37114         var rows = b.firstChild.rows;
37115         for(var i = 0, len = rows.length; i < len; i++){
37116             if(before){
37117                 tbody.insertBefore(rows[0], before);
37118             }else{
37119                 tbody.appendChild(rows[0]);
37120             }
37121         }
37122         b.innerHTML = "";
37123         b = null;
37124     },
37125
37126     deleteRows : function(dm, firstRow, lastRow){
37127         if(dm.getRowCount()<1){
37128             this.fireEvent("beforerefresh", this);
37129             this.mainBody.update("");
37130             this.lockedBody.update("");
37131             this.fireEvent("refresh", this);
37132         }else{
37133             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37134             var bt = this.getBodyTable();
37135             var tbody = bt.firstChild;
37136             var rows = bt.rows;
37137             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37138                 tbody.removeChild(rows[firstRow]);
37139             }
37140             this.stripeRows(firstRow);
37141             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37142         }
37143     },
37144
37145     updateRows : function(dataSource, firstRow, lastRow){
37146         var s = this.getScrollState();
37147         this.refresh();
37148         this.restoreScroll(s);
37149     },
37150
37151     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37152         if(!noRefresh){
37153            this.refresh();
37154         }
37155         this.updateHeaderSortState();
37156     },
37157
37158     getScrollState : function(){
37159         
37160         var sb = this.scroller.dom;
37161         return {left: sb.scrollLeft, top: sb.scrollTop};
37162     },
37163
37164     stripeRows : function(startRow){
37165         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37166             return;
37167         }
37168         startRow = startRow || 0;
37169         var rows = this.getBodyTable().rows;
37170         var lrows = this.getLockedTable().rows;
37171         var cls = ' x-grid-row-alt ';
37172         for(var i = startRow, len = rows.length; i < len; i++){
37173             var row = rows[i], lrow = lrows[i];
37174             var isAlt = ((i+1) % 2 == 0);
37175             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37176             if(isAlt == hasAlt){
37177                 continue;
37178             }
37179             if(isAlt){
37180                 row.className += " x-grid-row-alt";
37181             }else{
37182                 row.className = row.className.replace("x-grid-row-alt", "");
37183             }
37184             if(lrow){
37185                 lrow.className = row.className;
37186             }
37187         }
37188     },
37189
37190     restoreScroll : function(state){
37191         //Roo.log('GridView.restoreScroll');
37192         var sb = this.scroller.dom;
37193         sb.scrollLeft = state.left;
37194         sb.scrollTop = state.top;
37195         this.syncScroll();
37196     },
37197
37198     syncScroll : function(){
37199         //Roo.log('GridView.syncScroll');
37200         var sb = this.scroller.dom;
37201         var sh = this.mainHd.dom;
37202         var bs = this.mainBody.dom;
37203         var lv = this.lockedBody.dom;
37204         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37205         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37206     },
37207
37208     handleScroll : function(e){
37209         this.syncScroll();
37210         var sb = this.scroller.dom;
37211         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37212         e.stopEvent();
37213     },
37214
37215     handleWheel : function(e){
37216         var d = e.getWheelDelta();
37217         this.scroller.dom.scrollTop -= d*22;
37218         // set this here to prevent jumpy scrolling on large tables
37219         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37220         e.stopEvent();
37221     },
37222
37223     renderRows : function(startRow, endRow){
37224         // pull in all the crap needed to render rows
37225         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37226         var colCount = cm.getColumnCount();
37227
37228         if(ds.getCount() < 1){
37229             return ["", ""];
37230         }
37231
37232         // build a map for all the columns
37233         var cs = [];
37234         for(var i = 0; i < colCount; i++){
37235             var name = cm.getDataIndex(i);
37236             cs[i] = {
37237                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37238                 renderer : cm.getRenderer(i),
37239                 id : cm.getColumnId(i),
37240                 locked : cm.isLocked(i)
37241             };
37242         }
37243
37244         startRow = startRow || 0;
37245         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37246
37247         // records to render
37248         var rs = ds.getRange(startRow, endRow);
37249
37250         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37251     },
37252
37253     // As much as I hate to duplicate code, this was branched because FireFox really hates
37254     // [].join("") on strings. The performance difference was substantial enough to
37255     // branch this function
37256     doRender : Roo.isGecko ?
37257             function(cs, rs, ds, startRow, colCount, stripe){
37258                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37259                 // buffers
37260                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37261                 
37262                 var hasListener = this.grid.hasListener('rowclass');
37263                 var rowcfg = {};
37264                 for(var j = 0, len = rs.length; j < len; j++){
37265                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37266                     for(var i = 0; i < colCount; i++){
37267                         c = cs[i];
37268                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37269                         p.id = c.id;
37270                         p.css = p.attr = "";
37271                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37272                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37273                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37274                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37275                         }
37276                         var markup = ct.apply(p);
37277                         if(!c.locked){
37278                             cb+= markup;
37279                         }else{
37280                             lcb+= markup;
37281                         }
37282                     }
37283                     var alt = [];
37284                     if(stripe && ((rowIndex+1) % 2 == 0)){
37285                         alt.push("x-grid-row-alt")
37286                     }
37287                     if(r.dirty){
37288                         alt.push(  " x-grid-dirty-row");
37289                     }
37290                     rp.cells = lcb;
37291                     if(this.getRowClass){
37292                         alt.push(this.getRowClass(r, rowIndex));
37293                     }
37294                     if (hasListener) {
37295                         rowcfg = {
37296                              
37297                             record: r,
37298                             rowIndex : rowIndex,
37299                             rowClass : ''
37300                         }
37301                         this.grid.fireEvent('rowclass', this, rowcfg);
37302                         alt.push(rowcfg.rowClass);
37303                     }
37304                     rp.alt = alt.join(" ");
37305                     lbuf+= rt.apply(rp);
37306                     rp.cells = cb;
37307                     buf+=  rt.apply(rp);
37308                 }
37309                 return [lbuf, buf];
37310             } :
37311             function(cs, rs, ds, startRow, colCount, stripe){
37312                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37313                 // buffers
37314                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37315                 var hasListener = this.grid.hasListener('rowclass');
37316  
37317                 var rowcfg = {};
37318                 for(var j = 0, len = rs.length; j < len; j++){
37319                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37320                     for(var i = 0; i < colCount; i++){
37321                         c = cs[i];
37322                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37323                         p.id = c.id;
37324                         p.css = p.attr = "";
37325                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37326                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37327                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37328                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37329                         }
37330                         
37331                         var markup = ct.apply(p);
37332                         if(!c.locked){
37333                             cb[cb.length] = markup;
37334                         }else{
37335                             lcb[lcb.length] = markup;
37336                         }
37337                     }
37338                     var alt = [];
37339                     if(stripe && ((rowIndex+1) % 2 == 0)){
37340                         alt.push( "x-grid-row-alt");
37341                     }
37342                     if(r.dirty){
37343                         alt.push(" x-grid-dirty-row");
37344                     }
37345                     rp.cells = lcb;
37346                     if(this.getRowClass){
37347                         alt.push( this.getRowClass(r, rowIndex));
37348                     }
37349                     if (hasListener) {
37350                         rowcfg = {
37351                              
37352                             record: r,
37353                             rowIndex : rowIndex,
37354                             rowClass : ''
37355                         }
37356                         this.grid.fireEvent('rowclass', this, rowcfg);
37357                         alt.push(rowcfg.rowClass);
37358                     }
37359                     rp.alt = alt.join(" ");
37360                     rp.cells = lcb.join("");
37361                     lbuf[lbuf.length] = rt.apply(rp);
37362                     rp.cells = cb.join("");
37363                     buf[buf.length] =  rt.apply(rp);
37364                 }
37365                 return [lbuf.join(""), buf.join("")];
37366             },
37367
37368     renderBody : function(){
37369         var markup = this.renderRows();
37370         var bt = this.templates.body;
37371         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37372     },
37373
37374     /**
37375      * Refreshes the grid
37376      * @param {Boolean} headersToo
37377      */
37378     refresh : function(headersToo){
37379         this.fireEvent("beforerefresh", this);
37380         this.grid.stopEditing();
37381         var result = this.renderBody();
37382         this.lockedBody.update(result[0]);
37383         this.mainBody.update(result[1]);
37384         if(headersToo === true){
37385             this.updateHeaders();
37386             this.updateColumns();
37387             this.updateSplitters();
37388             this.updateHeaderSortState();
37389         }
37390         this.syncRowHeights();
37391         this.layout();
37392         this.fireEvent("refresh", this);
37393     },
37394
37395     handleColumnMove : function(cm, oldIndex, newIndex){
37396         this.indexMap = null;
37397         var s = this.getScrollState();
37398         this.refresh(true);
37399         this.restoreScroll(s);
37400         this.afterMove(newIndex);
37401     },
37402
37403     afterMove : function(colIndex){
37404         if(this.enableMoveAnim && Roo.enableFx){
37405             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37406         }
37407         // if multisort - fix sortOrder, and reload..
37408         if (this.grid.dataSource.multiSort) {
37409             // the we can call sort again..
37410             var dm = this.grid.dataSource;
37411             var cm = this.grid.colModel;
37412             var so = [];
37413             for(var i = 0; i < cm.config.length; i++ ) {
37414                 
37415                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37416                     continue; // dont' bother, it's not in sort list or being set.
37417                 }
37418                 
37419                 so.push(cm.config[i].dataIndex);
37420             };
37421             dm.sortOrder = so;
37422             dm.load(dm.lastOptions);
37423             
37424             
37425         }
37426         
37427     },
37428
37429     updateCell : function(dm, rowIndex, dataIndex){
37430         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37431         if(typeof colIndex == "undefined"){ // not present in grid
37432             return;
37433         }
37434         var cm = this.grid.colModel;
37435         var cell = this.getCell(rowIndex, colIndex);
37436         var cellText = this.getCellText(rowIndex, colIndex);
37437
37438         var p = {
37439             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37440             id : cm.getColumnId(colIndex),
37441             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37442         };
37443         var renderer = cm.getRenderer(colIndex);
37444         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37445         if(typeof val == "undefined" || val === "") val = "&#160;";
37446         cellText.innerHTML = val;
37447         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37448         this.syncRowHeights(rowIndex, rowIndex);
37449     },
37450
37451     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37452         var maxWidth = 0;
37453         if(this.grid.autoSizeHeaders){
37454             var h = this.getHeaderCellMeasure(colIndex);
37455             maxWidth = Math.max(maxWidth, h.scrollWidth);
37456         }
37457         var tb, index;
37458         if(this.cm.isLocked(colIndex)){
37459             tb = this.getLockedTable();
37460             index = colIndex;
37461         }else{
37462             tb = this.getBodyTable();
37463             index = colIndex - this.cm.getLockedCount();
37464         }
37465         if(tb && tb.rows){
37466             var rows = tb.rows;
37467             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37468             for(var i = 0; i < stopIndex; i++){
37469                 var cell = rows[i].childNodes[index].firstChild;
37470                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37471             }
37472         }
37473         return maxWidth + /*margin for error in IE*/ 5;
37474     },
37475     /**
37476      * Autofit a column to its content.
37477      * @param {Number} colIndex
37478      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37479      */
37480      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37481          if(this.cm.isHidden(colIndex)){
37482              return; // can't calc a hidden column
37483          }
37484         if(forceMinSize){
37485             var cid = this.cm.getColumnId(colIndex);
37486             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37487            if(this.grid.autoSizeHeaders){
37488                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37489            }
37490         }
37491         var newWidth = this.calcColumnWidth(colIndex);
37492         this.cm.setColumnWidth(colIndex,
37493             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37494         if(!suppressEvent){
37495             this.grid.fireEvent("columnresize", colIndex, newWidth);
37496         }
37497     },
37498
37499     /**
37500      * Autofits all columns to their content and then expands to fit any extra space in the grid
37501      */
37502      autoSizeColumns : function(){
37503         var cm = this.grid.colModel;
37504         var colCount = cm.getColumnCount();
37505         for(var i = 0; i < colCount; i++){
37506             this.autoSizeColumn(i, true, true);
37507         }
37508         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37509             this.fitColumns();
37510         }else{
37511             this.updateColumns();
37512             this.layout();
37513         }
37514     },
37515
37516     /**
37517      * Autofits all columns to the grid's width proportionate with their current size
37518      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37519      */
37520     fitColumns : function(reserveScrollSpace){
37521         var cm = this.grid.colModel;
37522         var colCount = cm.getColumnCount();
37523         var cols = [];
37524         var width = 0;
37525         var i, w;
37526         for (i = 0; i < colCount; i++){
37527             if(!cm.isHidden(i) && !cm.isFixed(i)){
37528                 w = cm.getColumnWidth(i);
37529                 cols.push(i);
37530                 cols.push(w);
37531                 width += w;
37532             }
37533         }
37534         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37535         if(reserveScrollSpace){
37536             avail -= 17;
37537         }
37538         var frac = (avail - cm.getTotalWidth())/width;
37539         while (cols.length){
37540             w = cols.pop();
37541             i = cols.pop();
37542             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37543         }
37544         this.updateColumns();
37545         this.layout();
37546     },
37547
37548     onRowSelect : function(rowIndex){
37549         var row = this.getRowComposite(rowIndex);
37550         row.addClass("x-grid-row-selected");
37551     },
37552
37553     onRowDeselect : function(rowIndex){
37554         var row = this.getRowComposite(rowIndex);
37555         row.removeClass("x-grid-row-selected");
37556     },
37557
37558     onCellSelect : function(row, col){
37559         var cell = this.getCell(row, col);
37560         if(cell){
37561             Roo.fly(cell).addClass("x-grid-cell-selected");
37562         }
37563     },
37564
37565     onCellDeselect : function(row, col){
37566         var cell = this.getCell(row, col);
37567         if(cell){
37568             Roo.fly(cell).removeClass("x-grid-cell-selected");
37569         }
37570     },
37571
37572     updateHeaderSortState : function(){
37573         
37574         // sort state can be single { field: xxx, direction : yyy}
37575         // or   { xxx=>ASC , yyy : DESC ..... }
37576         
37577         var mstate = {};
37578         if (!this.ds.multiSort) { 
37579             var state = this.ds.getSortState();
37580             if(!state){
37581                 return;
37582             }
37583             mstate[state.field] = state.direction;
37584             // FIXME... - this is not used here.. but might be elsewhere..
37585             this.sortState = state;
37586             
37587         } else {
37588             mstate = this.ds.sortToggle;
37589         }
37590         //remove existing sort classes..
37591         
37592         var sc = this.sortClasses;
37593         var hds = this.el.select(this.headerSelector).removeClass(sc);
37594         
37595         for(var f in mstate) {
37596         
37597             var sortColumn = this.cm.findColumnIndex(f);
37598             
37599             if(sortColumn != -1){
37600                 var sortDir = mstate[f];        
37601                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37602             }
37603         }
37604         
37605          
37606         
37607     },
37608
37609
37610     handleHeaderClick : function(g, index){
37611         if(this.headersDisabled){
37612             return;
37613         }
37614         var dm = g.dataSource, cm = g.colModel;
37615         if(!cm.isSortable(index)){
37616             return;
37617         }
37618         g.stopEditing();
37619         
37620         if (dm.multiSort) {
37621             // update the sortOrder
37622             var so = [];
37623             for(var i = 0; i < cm.config.length; i++ ) {
37624                 
37625                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37626                     continue; // dont' bother, it's not in sort list or being set.
37627                 }
37628                 
37629                 so.push(cm.config[i].dataIndex);
37630             };
37631             dm.sortOrder = so;
37632         }
37633         
37634         
37635         dm.sort(cm.getDataIndex(index));
37636     },
37637
37638
37639     destroy : function(){
37640         if(this.colMenu){
37641             this.colMenu.removeAll();
37642             Roo.menu.MenuMgr.unregister(this.colMenu);
37643             this.colMenu.getEl().remove();
37644             delete this.colMenu;
37645         }
37646         if(this.hmenu){
37647             this.hmenu.removeAll();
37648             Roo.menu.MenuMgr.unregister(this.hmenu);
37649             this.hmenu.getEl().remove();
37650             delete this.hmenu;
37651         }
37652         if(this.grid.enableColumnMove){
37653             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37654             if(dds){
37655                 for(var dd in dds){
37656                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
37657                         var elid = dds[dd].dragElId;
37658                         dds[dd].unreg();
37659                         Roo.get(elid).remove();
37660                     } else if(dds[dd].config.isTarget){
37661                         dds[dd].proxyTop.remove();
37662                         dds[dd].proxyBottom.remove();
37663                         dds[dd].unreg();
37664                     }
37665                     if(Roo.dd.DDM.locationCache[dd]){
37666                         delete Roo.dd.DDM.locationCache[dd];
37667                     }
37668                 }
37669                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37670             }
37671         }
37672         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37673         this.bind(null, null);
37674         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37675     },
37676
37677     handleLockChange : function(){
37678         this.refresh(true);
37679     },
37680
37681     onDenyColumnLock : function(){
37682
37683     },
37684
37685     onDenyColumnHide : function(){
37686
37687     },
37688
37689     handleHdMenuClick : function(item){
37690         var index = this.hdCtxIndex;
37691         var cm = this.cm, ds = this.ds;
37692         switch(item.id){
37693             case "asc":
37694                 ds.sort(cm.getDataIndex(index), "ASC");
37695                 break;
37696             case "desc":
37697                 ds.sort(cm.getDataIndex(index), "DESC");
37698                 break;
37699             case "lock":
37700                 var lc = cm.getLockedCount();
37701                 if(cm.getColumnCount(true) <= lc+1){
37702                     this.onDenyColumnLock();
37703                     return;
37704                 }
37705                 if(lc != index){
37706                     cm.setLocked(index, true, true);
37707                     cm.moveColumn(index, lc);
37708                     this.grid.fireEvent("columnmove", index, lc);
37709                 }else{
37710                     cm.setLocked(index, true);
37711                 }
37712             break;
37713             case "unlock":
37714                 var lc = cm.getLockedCount();
37715                 if((lc-1) != index){
37716                     cm.setLocked(index, false, true);
37717                     cm.moveColumn(index, lc-1);
37718                     this.grid.fireEvent("columnmove", index, lc-1);
37719                 }else{
37720                     cm.setLocked(index, false);
37721                 }
37722             break;
37723             default:
37724                 index = cm.getIndexById(item.id.substr(4));
37725                 if(index != -1){
37726                     if(item.checked && cm.getColumnCount(true) <= 1){
37727                         this.onDenyColumnHide();
37728                         return false;
37729                     }
37730                     cm.setHidden(index, item.checked);
37731                 }
37732         }
37733         return true;
37734     },
37735
37736     beforeColMenuShow : function(){
37737         var cm = this.cm,  colCount = cm.getColumnCount();
37738         this.colMenu.removeAll();
37739         for(var i = 0; i < colCount; i++){
37740             this.colMenu.add(new Roo.menu.CheckItem({
37741                 id: "col-"+cm.getColumnId(i),
37742                 text: cm.getColumnHeader(i),
37743                 checked: !cm.isHidden(i),
37744                 hideOnClick:false
37745             }));
37746         }
37747     },
37748
37749     handleHdCtx : function(g, index, e){
37750         e.stopEvent();
37751         var hd = this.getHeaderCell(index);
37752         this.hdCtxIndex = index;
37753         var ms = this.hmenu.items, cm = this.cm;
37754         ms.get("asc").setDisabled(!cm.isSortable(index));
37755         ms.get("desc").setDisabled(!cm.isSortable(index));
37756         if(this.grid.enableColLock !== false){
37757             ms.get("lock").setDisabled(cm.isLocked(index));
37758             ms.get("unlock").setDisabled(!cm.isLocked(index));
37759         }
37760         this.hmenu.show(hd, "tl-bl");
37761     },
37762
37763     handleHdOver : function(e){
37764         var hd = this.findHeaderCell(e.getTarget());
37765         if(hd && !this.headersDisabled){
37766             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37767                this.fly(hd).addClass("x-grid-hd-over");
37768             }
37769         }
37770     },
37771
37772     handleHdOut : function(e){
37773         var hd = this.findHeaderCell(e.getTarget());
37774         if(hd){
37775             this.fly(hd).removeClass("x-grid-hd-over");
37776         }
37777     },
37778
37779     handleSplitDblClick : function(e, t){
37780         var i = this.getCellIndex(t);
37781         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37782             this.autoSizeColumn(i, true);
37783             this.layout();
37784         }
37785     },
37786
37787     render : function(){
37788
37789         var cm = this.cm;
37790         var colCount = cm.getColumnCount();
37791
37792         if(this.grid.monitorWindowResize === true){
37793             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37794         }
37795         var header = this.renderHeaders();
37796         var body = this.templates.body.apply({rows:""});
37797         var html = this.templates.master.apply({
37798             lockedBody: body,
37799             body: body,
37800             lockedHeader: header[0],
37801             header: header[1]
37802         });
37803
37804         //this.updateColumns();
37805
37806         this.grid.getGridEl().dom.innerHTML = html;
37807
37808         this.initElements();
37809         
37810         // a kludge to fix the random scolling effect in webkit
37811         this.el.on("scroll", function() {
37812             this.el.dom.scrollTop=0; // hopefully not recursive..
37813         },this);
37814
37815         this.scroller.on("scroll", this.handleScroll, this);
37816         this.lockedBody.on("mousewheel", this.handleWheel, this);
37817         this.mainBody.on("mousewheel", this.handleWheel, this);
37818
37819         this.mainHd.on("mouseover", this.handleHdOver, this);
37820         this.mainHd.on("mouseout", this.handleHdOut, this);
37821         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37822                 {delegate: "."+this.splitClass});
37823
37824         this.lockedHd.on("mouseover", this.handleHdOver, this);
37825         this.lockedHd.on("mouseout", this.handleHdOut, this);
37826         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37827                 {delegate: "."+this.splitClass});
37828
37829         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
37830             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37831         }
37832
37833         this.updateSplitters();
37834
37835         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
37836             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37837             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37838         }
37839
37840         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
37841             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
37842             this.hmenu.add(
37843                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
37844                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
37845             );
37846             if(this.grid.enableColLock !== false){
37847                 this.hmenu.add('-',
37848                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
37849                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
37850                 );
37851             }
37852             if(this.grid.enableColumnHide !== false){
37853
37854                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
37855                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
37856                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
37857
37858                 this.hmenu.add('-',
37859                     {id:"columns", text: this.columnsText, menu: this.colMenu}
37860                 );
37861             }
37862             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
37863
37864             this.grid.on("headercontextmenu", this.handleHdCtx, this);
37865         }
37866
37867         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
37868             this.dd = new Roo.grid.GridDragZone(this.grid, {
37869                 ddGroup : this.grid.ddGroup || 'GridDD'
37870             });
37871             
37872         }
37873
37874         /*
37875         for(var i = 0; i < colCount; i++){
37876             if(cm.isHidden(i)){
37877                 this.hideColumn(i);
37878             }
37879             if(cm.config[i].align){
37880                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
37881                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
37882             }
37883         }*/
37884         
37885         this.updateHeaderSortState();
37886
37887         this.beforeInitialResize();
37888         this.layout(true);
37889
37890         // two part rendering gives faster view to the user
37891         this.renderPhase2.defer(1, this);
37892     },
37893
37894     renderPhase2 : function(){
37895         // render the rows now
37896         this.refresh();
37897         if(this.grid.autoSizeColumns){
37898             this.autoSizeColumns();
37899         }
37900     },
37901
37902     beforeInitialResize : function(){
37903
37904     },
37905
37906     onColumnSplitterMoved : function(i, w){
37907         this.userResized = true;
37908         var cm = this.grid.colModel;
37909         cm.setColumnWidth(i, w, true);
37910         var cid = cm.getColumnId(i);
37911         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37912         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37913         this.updateSplitters();
37914         this.layout();
37915         this.grid.fireEvent("columnresize", i, w);
37916     },
37917
37918     syncRowHeights : function(startIndex, endIndex){
37919         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
37920             startIndex = startIndex || 0;
37921             var mrows = this.getBodyTable().rows;
37922             var lrows = this.getLockedTable().rows;
37923             var len = mrows.length-1;
37924             endIndex = Math.min(endIndex || len, len);
37925             for(var i = startIndex; i <= endIndex; i++){
37926                 var m = mrows[i], l = lrows[i];
37927                 var h = Math.max(m.offsetHeight, l.offsetHeight);
37928                 m.style.height = l.style.height = h + "px";
37929             }
37930         }
37931     },
37932
37933     layout : function(initialRender, is2ndPass){
37934         var g = this.grid;
37935         var auto = g.autoHeight;
37936         var scrollOffset = 16;
37937         var c = g.getGridEl(), cm = this.cm,
37938                 expandCol = g.autoExpandColumn,
37939                 gv = this;
37940         //c.beginMeasure();
37941
37942         if(!c.dom.offsetWidth){ // display:none?
37943             if(initialRender){
37944                 this.lockedWrap.show();
37945                 this.mainWrap.show();
37946             }
37947             return;
37948         }
37949
37950         var hasLock = this.cm.isLocked(0);
37951
37952         var tbh = this.headerPanel.getHeight();
37953         var bbh = this.footerPanel.getHeight();
37954
37955         if(auto){
37956             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
37957             var newHeight = ch + c.getBorderWidth("tb");
37958             if(g.maxHeight){
37959                 newHeight = Math.min(g.maxHeight, newHeight);
37960             }
37961             c.setHeight(newHeight);
37962         }
37963
37964         if(g.autoWidth){
37965             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
37966         }
37967
37968         var s = this.scroller;
37969
37970         var csize = c.getSize(true);
37971
37972         this.el.setSize(csize.width, csize.height);
37973
37974         this.headerPanel.setWidth(csize.width);
37975         this.footerPanel.setWidth(csize.width);
37976
37977         var hdHeight = this.mainHd.getHeight();
37978         var vw = csize.width;
37979         var vh = csize.height - (tbh + bbh);
37980
37981         s.setSize(vw, vh);
37982
37983         var bt = this.getBodyTable();
37984         var ltWidth = hasLock ?
37985                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
37986
37987         var scrollHeight = bt.offsetHeight;
37988         var scrollWidth = ltWidth + bt.offsetWidth;
37989         var vscroll = false, hscroll = false;
37990
37991         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
37992
37993         var lw = this.lockedWrap, mw = this.mainWrap;
37994         var lb = this.lockedBody, mb = this.mainBody;
37995
37996         setTimeout(function(){
37997             var t = s.dom.offsetTop;
37998             var w = s.dom.clientWidth,
37999                 h = s.dom.clientHeight;
38000
38001             lw.setTop(t);
38002             lw.setSize(ltWidth, h);
38003
38004             mw.setLeftTop(ltWidth, t);
38005             mw.setSize(w-ltWidth, h);
38006
38007             lb.setHeight(h-hdHeight);
38008             mb.setHeight(h-hdHeight);
38009
38010             if(is2ndPass !== true && !gv.userResized && expandCol){
38011                 // high speed resize without full column calculation
38012                 
38013                 var ci = cm.getIndexById(expandCol);
38014                 if (ci < 0) {
38015                     ci = cm.findColumnIndex(expandCol);
38016                 }
38017                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38018                 var expandId = cm.getColumnId(ci);
38019                 var  tw = cm.getTotalWidth(false);
38020                 var currentWidth = cm.getColumnWidth(ci);
38021                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38022                 if(currentWidth != cw){
38023                     cm.setColumnWidth(ci, cw, true);
38024                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38025                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38026                     gv.updateSplitters();
38027                     gv.layout(false, true);
38028                 }
38029             }
38030
38031             if(initialRender){
38032                 lw.show();
38033                 mw.show();
38034             }
38035             //c.endMeasure();
38036         }, 10);
38037     },
38038
38039     onWindowResize : function(){
38040         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38041             return;
38042         }
38043         this.layout();
38044     },
38045
38046     appendFooter : function(parentEl){
38047         return null;
38048     },
38049
38050     sortAscText : "Sort Ascending",
38051     sortDescText : "Sort Descending",
38052     lockText : "Lock Column",
38053     unlockText : "Unlock Column",
38054     columnsText : "Columns"
38055 });
38056
38057
38058 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38059     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38060     this.proxy.el.addClass('x-grid3-col-dd');
38061 };
38062
38063 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38064     handleMouseDown : function(e){
38065
38066     },
38067
38068     callHandleMouseDown : function(e){
38069         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38070     }
38071 });
38072 /*
38073  * Based on:
38074  * Ext JS Library 1.1.1
38075  * Copyright(c) 2006-2007, Ext JS, LLC.
38076  *
38077  * Originally Released Under LGPL - original licence link has changed is not relivant.
38078  *
38079  * Fork - LGPL
38080  * <script type="text/javascript">
38081  */
38082  
38083 // private
38084 // This is a support class used internally by the Grid components
38085 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38086     this.grid = grid;
38087     this.view = grid.getView();
38088     this.proxy = this.view.resizeProxy;
38089     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38090         "gridSplitters" + this.grid.getGridEl().id, {
38091         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38092     });
38093     this.setHandleElId(Roo.id(hd));
38094     this.setOuterHandleElId(Roo.id(hd2));
38095     this.scroll = false;
38096 };
38097 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38098     fly: Roo.Element.fly,
38099
38100     b4StartDrag : function(x, y){
38101         this.view.headersDisabled = true;
38102         this.proxy.setHeight(this.view.mainWrap.getHeight());
38103         var w = this.cm.getColumnWidth(this.cellIndex);
38104         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38105         this.resetConstraints();
38106         this.setXConstraint(minw, 1000);
38107         this.setYConstraint(0, 0);
38108         this.minX = x - minw;
38109         this.maxX = x + 1000;
38110         this.startPos = x;
38111         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38112     },
38113
38114
38115     handleMouseDown : function(e){
38116         ev = Roo.EventObject.setEvent(e);
38117         var t = this.fly(ev.getTarget());
38118         if(t.hasClass("x-grid-split")){
38119             this.cellIndex = this.view.getCellIndex(t.dom);
38120             this.split = t.dom;
38121             this.cm = this.grid.colModel;
38122             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38123                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38124             }
38125         }
38126     },
38127
38128     endDrag : function(e){
38129         this.view.headersDisabled = false;
38130         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38131         var diff = endX - this.startPos;
38132         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38133     },
38134
38135     autoOffset : function(){
38136         this.setDelta(0,0);
38137     }
38138 });/*
38139  * Based on:
38140  * Ext JS Library 1.1.1
38141  * Copyright(c) 2006-2007, Ext JS, LLC.
38142  *
38143  * Originally Released Under LGPL - original licence link has changed is not relivant.
38144  *
38145  * Fork - LGPL
38146  * <script type="text/javascript">
38147  */
38148  
38149 // private
38150 // This is a support class used internally by the Grid components
38151 Roo.grid.GridDragZone = function(grid, config){
38152     this.view = grid.getView();
38153     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38154     if(this.view.lockedBody){
38155         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38156         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38157     }
38158     this.scroll = false;
38159     this.grid = grid;
38160     this.ddel = document.createElement('div');
38161     this.ddel.className = 'x-grid-dd-wrap';
38162 };
38163
38164 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38165     ddGroup : "GridDD",
38166
38167     getDragData : function(e){
38168         var t = Roo.lib.Event.getTarget(e);
38169         var rowIndex = this.view.findRowIndex(t);
38170         var sm = this.grid.selModel;
38171             
38172         //Roo.log(rowIndex);
38173         
38174         if (sm.getSelectedCell) {
38175             // cell selection..
38176             if (!sm.getSelectedCell()) {
38177                 return false;
38178             }
38179             if (rowIndex != sm.getSelectedCell()[0]) {
38180                 return false;
38181             }
38182         
38183         }
38184         
38185         if(rowIndex !== false){
38186             
38187             // if editorgrid.. 
38188             
38189             
38190             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38191                
38192             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38193               //  
38194             //}
38195             if (e.hasModifier()){
38196                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38197             }
38198             
38199             Roo.log("getDragData");
38200             
38201             return {
38202                 grid: this.grid,
38203                 ddel: this.ddel,
38204                 rowIndex: rowIndex,
38205                 selections:sm.getSelections ? sm.getSelections() : (
38206                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38207                 )
38208             };
38209         }
38210         return false;
38211     },
38212
38213     onInitDrag : function(e){
38214         var data = this.dragData;
38215         this.ddel.innerHTML = this.grid.getDragDropText();
38216         this.proxy.update(this.ddel);
38217         // fire start drag?
38218     },
38219
38220     afterRepair : function(){
38221         this.dragging = false;
38222     },
38223
38224     getRepairXY : function(e, data){
38225         return false;
38226     },
38227
38228     onEndDrag : function(data, e){
38229         // fire end drag?
38230     },
38231
38232     onValidDrop : function(dd, e, id){
38233         // fire drag drop?
38234         this.hideProxy();
38235     },
38236
38237     beforeInvalidDrop : function(e, id){
38238
38239     }
38240 });/*
38241  * Based on:
38242  * Ext JS Library 1.1.1
38243  * Copyright(c) 2006-2007, Ext JS, LLC.
38244  *
38245  * Originally Released Under LGPL - original licence link has changed is not relivant.
38246  *
38247  * Fork - LGPL
38248  * <script type="text/javascript">
38249  */
38250  
38251
38252 /**
38253  * @class Roo.grid.ColumnModel
38254  * @extends Roo.util.Observable
38255  * This is the default implementation of a ColumnModel used by the Grid. It defines
38256  * the columns in the grid.
38257  * <br>Usage:<br>
38258  <pre><code>
38259  var colModel = new Roo.grid.ColumnModel([
38260         {header: "Ticker", width: 60, sortable: true, locked: true},
38261         {header: "Company Name", width: 150, sortable: true},
38262         {header: "Market Cap.", width: 100, sortable: true},
38263         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38264         {header: "Employees", width: 100, sortable: true, resizable: false}
38265  ]);
38266  </code></pre>
38267  * <p>
38268  
38269  * The config options listed for this class are options which may appear in each
38270  * individual column definition.
38271  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38272  * @constructor
38273  * @param {Object} config An Array of column config objects. See this class's
38274  * config objects for details.
38275 */
38276 Roo.grid.ColumnModel = function(config){
38277         /**
38278      * The config passed into the constructor
38279      */
38280     this.config = config;
38281     this.lookup = {};
38282
38283     // if no id, create one
38284     // if the column does not have a dataIndex mapping,
38285     // map it to the order it is in the config
38286     for(var i = 0, len = config.length; i < len; i++){
38287         var c = config[i];
38288         if(typeof c.dataIndex == "undefined"){
38289             c.dataIndex = i;
38290         }
38291         if(typeof c.renderer == "string"){
38292             c.renderer = Roo.util.Format[c.renderer];
38293         }
38294         if(typeof c.id == "undefined"){
38295             c.id = Roo.id();
38296         }
38297         if(c.editor && c.editor.xtype){
38298             c.editor  = Roo.factory(c.editor, Roo.grid);
38299         }
38300         if(c.editor && c.editor.isFormField){
38301             c.editor = new Roo.grid.GridEditor(c.editor);
38302         }
38303         this.lookup[c.id] = c;
38304     }
38305
38306     /**
38307      * The width of columns which have no width specified (defaults to 100)
38308      * @type Number
38309      */
38310     this.defaultWidth = 100;
38311
38312     /**
38313      * Default sortable of columns which have no sortable specified (defaults to false)
38314      * @type Boolean
38315      */
38316     this.defaultSortable = false;
38317
38318     this.addEvents({
38319         /**
38320              * @event widthchange
38321              * Fires when the width of a column changes.
38322              * @param {ColumnModel} this
38323              * @param {Number} columnIndex The column index
38324              * @param {Number} newWidth The new width
38325              */
38326             "widthchange": true,
38327         /**
38328              * @event headerchange
38329              * Fires when the text of a header changes.
38330              * @param {ColumnModel} this
38331              * @param {Number} columnIndex The column index
38332              * @param {Number} newText The new header text
38333              */
38334             "headerchange": true,
38335         /**
38336              * @event hiddenchange
38337              * Fires when a column is hidden or "unhidden".
38338              * @param {ColumnModel} this
38339              * @param {Number} columnIndex The column index
38340              * @param {Boolean} hidden true if hidden, false otherwise
38341              */
38342             "hiddenchange": true,
38343             /**
38344          * @event columnmoved
38345          * Fires when a column is moved.
38346          * @param {ColumnModel} this
38347          * @param {Number} oldIndex
38348          * @param {Number} newIndex
38349          */
38350         "columnmoved" : true,
38351         /**
38352          * @event columlockchange
38353          * Fires when a column's locked state is changed
38354          * @param {ColumnModel} this
38355          * @param {Number} colIndex
38356          * @param {Boolean} locked true if locked
38357          */
38358         "columnlockchange" : true
38359     });
38360     Roo.grid.ColumnModel.superclass.constructor.call(this);
38361 };
38362 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38363     /**
38364      * @cfg {String} header The header text to display in the Grid view.
38365      */
38366     /**
38367      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38368      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38369      * specified, the column's index is used as an index into the Record's data Array.
38370      */
38371     /**
38372      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38373      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38374      */
38375     /**
38376      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38377      * Defaults to the value of the {@link #defaultSortable} property.
38378      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38379      */
38380     /**
38381      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38382      */
38383     /**
38384      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38385      */
38386     /**
38387      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38388      */
38389     /**
38390      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38391      */
38392     /**
38393      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38394      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38395      * default renderer uses the raw data value.
38396      */
38397        /**
38398      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38399      */
38400     /**
38401      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38402      */
38403
38404     /**
38405      * Returns the id of the column at the specified index.
38406      * @param {Number} index The column index
38407      * @return {String} the id
38408      */
38409     getColumnId : function(index){
38410         return this.config[index].id;
38411     },
38412
38413     /**
38414      * Returns the column for a specified id.
38415      * @param {String} id The column id
38416      * @return {Object} the column
38417      */
38418     getColumnById : function(id){
38419         return this.lookup[id];
38420     },
38421
38422     
38423     /**
38424      * Returns the column for a specified dataIndex.
38425      * @param {String} dataIndex The column dataIndex
38426      * @return {Object|Boolean} the column or false if not found
38427      */
38428     getColumnByDataIndex: function(dataIndex){
38429         var index = this.findColumnIndex(dataIndex);
38430         return index > -1 ? this.config[index] : false;
38431     },
38432     
38433     /**
38434      * Returns the index for a specified column id.
38435      * @param {String} id The column id
38436      * @return {Number} the index, or -1 if not found
38437      */
38438     getIndexById : function(id){
38439         for(var i = 0, len = this.config.length; i < len; i++){
38440             if(this.config[i].id == id){
38441                 return i;
38442             }
38443         }
38444         return -1;
38445     },
38446     
38447     /**
38448      * Returns the index for a specified column dataIndex.
38449      * @param {String} dataIndex The column dataIndex
38450      * @return {Number} the index, or -1 if not found
38451      */
38452     
38453     findColumnIndex : function(dataIndex){
38454         for(var i = 0, len = this.config.length; i < len; i++){
38455             if(this.config[i].dataIndex == dataIndex){
38456                 return i;
38457             }
38458         }
38459         return -1;
38460     },
38461     
38462     
38463     moveColumn : function(oldIndex, newIndex){
38464         var c = this.config[oldIndex];
38465         this.config.splice(oldIndex, 1);
38466         this.config.splice(newIndex, 0, c);
38467         this.dataMap = null;
38468         this.fireEvent("columnmoved", this, oldIndex, newIndex);
38469     },
38470
38471     isLocked : function(colIndex){
38472         return this.config[colIndex].locked === true;
38473     },
38474
38475     setLocked : function(colIndex, value, suppressEvent){
38476         if(this.isLocked(colIndex) == value){
38477             return;
38478         }
38479         this.config[colIndex].locked = value;
38480         if(!suppressEvent){
38481             this.fireEvent("columnlockchange", this, colIndex, value);
38482         }
38483     },
38484
38485     getTotalLockedWidth : function(){
38486         var totalWidth = 0;
38487         for(var i = 0; i < this.config.length; i++){
38488             if(this.isLocked(i) && !this.isHidden(i)){
38489                 this.totalWidth += this.getColumnWidth(i);
38490             }
38491         }
38492         return totalWidth;
38493     },
38494
38495     getLockedCount : function(){
38496         for(var i = 0, len = this.config.length; i < len; i++){
38497             if(!this.isLocked(i)){
38498                 return i;
38499             }
38500         }
38501     },
38502
38503     /**
38504      * Returns the number of columns.
38505      * @return {Number}
38506      */
38507     getColumnCount : function(visibleOnly){
38508         if(visibleOnly === true){
38509             var c = 0;
38510             for(var i = 0, len = this.config.length; i < len; i++){
38511                 if(!this.isHidden(i)){
38512                     c++;
38513                 }
38514             }
38515             return c;
38516         }
38517         return this.config.length;
38518     },
38519
38520     /**
38521      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38522      * @param {Function} fn
38523      * @param {Object} scope (optional)
38524      * @return {Array} result
38525      */
38526     getColumnsBy : function(fn, scope){
38527         var r = [];
38528         for(var i = 0, len = this.config.length; i < len; i++){
38529             var c = this.config[i];
38530             if(fn.call(scope||this, c, i) === true){
38531                 r[r.length] = c;
38532             }
38533         }
38534         return r;
38535     },
38536
38537     /**
38538      * Returns true if the specified column is sortable.
38539      * @param {Number} col The column index
38540      * @return {Boolean}
38541      */
38542     isSortable : function(col){
38543         if(typeof this.config[col].sortable == "undefined"){
38544             return this.defaultSortable;
38545         }
38546         return this.config[col].sortable;
38547     },
38548
38549     /**
38550      * Returns the rendering (formatting) function defined for the column.
38551      * @param {Number} col The column index.
38552      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38553      */
38554     getRenderer : function(col){
38555         if(!this.config[col].renderer){
38556             return Roo.grid.ColumnModel.defaultRenderer;
38557         }
38558         return this.config[col].renderer;
38559     },
38560
38561     /**
38562      * Sets the rendering (formatting) function for a column.
38563      * @param {Number} col The column index
38564      * @param {Function} fn The function to use to process the cell's raw data
38565      * to return HTML markup for the grid view. The render function is called with
38566      * the following parameters:<ul>
38567      * <li>Data value.</li>
38568      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38569      * <li>css A CSS style string to apply to the table cell.</li>
38570      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38571      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38572      * <li>Row index</li>
38573      * <li>Column index</li>
38574      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38575      */
38576     setRenderer : function(col, fn){
38577         this.config[col].renderer = fn;
38578     },
38579
38580     /**
38581      * Returns the width for the specified column.
38582      * @param {Number} col The column index
38583      * @return {Number}
38584      */
38585     getColumnWidth : function(col){
38586         return this.config[col].width * 1 || this.defaultWidth;
38587     },
38588
38589     /**
38590      * Sets the width for a column.
38591      * @param {Number} col The column index
38592      * @param {Number} width The new width
38593      */
38594     setColumnWidth : function(col, width, suppressEvent){
38595         this.config[col].width = width;
38596         this.totalWidth = null;
38597         if(!suppressEvent){
38598              this.fireEvent("widthchange", this, col, width);
38599         }
38600     },
38601
38602     /**
38603      * Returns the total width of all columns.
38604      * @param {Boolean} includeHidden True to include hidden column widths
38605      * @return {Number}
38606      */
38607     getTotalWidth : function(includeHidden){
38608         if(!this.totalWidth){
38609             this.totalWidth = 0;
38610             for(var i = 0, len = this.config.length; i < len; i++){
38611                 if(includeHidden || !this.isHidden(i)){
38612                     this.totalWidth += this.getColumnWidth(i);
38613                 }
38614             }
38615         }
38616         return this.totalWidth;
38617     },
38618
38619     /**
38620      * Returns the header for the specified column.
38621      * @param {Number} col The column index
38622      * @return {String}
38623      */
38624     getColumnHeader : function(col){
38625         return this.config[col].header;
38626     },
38627
38628     /**
38629      * Sets the header for a column.
38630      * @param {Number} col The column index
38631      * @param {String} header The new header
38632      */
38633     setColumnHeader : function(col, header){
38634         this.config[col].header = header;
38635         this.fireEvent("headerchange", this, col, header);
38636     },
38637
38638     /**
38639      * Returns the tooltip for the specified column.
38640      * @param {Number} col The column index
38641      * @return {String}
38642      */
38643     getColumnTooltip : function(col){
38644             return this.config[col].tooltip;
38645     },
38646     /**
38647      * Sets the tooltip for a column.
38648      * @param {Number} col The column index
38649      * @param {String} tooltip The new tooltip
38650      */
38651     setColumnTooltip : function(col, tooltip){
38652             this.config[col].tooltip = tooltip;
38653     },
38654
38655     /**
38656      * Returns the dataIndex for the specified column.
38657      * @param {Number} col The column index
38658      * @return {Number}
38659      */
38660     getDataIndex : function(col){
38661         return this.config[col].dataIndex;
38662     },
38663
38664     /**
38665      * Sets the dataIndex for a column.
38666      * @param {Number} col The column index
38667      * @param {Number} dataIndex The new dataIndex
38668      */
38669     setDataIndex : function(col, dataIndex){
38670         this.config[col].dataIndex = dataIndex;
38671     },
38672
38673     
38674     
38675     /**
38676      * Returns true if the cell is editable.
38677      * @param {Number} colIndex The column index
38678      * @param {Number} rowIndex The row index
38679      * @return {Boolean}
38680      */
38681     isCellEditable : function(colIndex, rowIndex){
38682         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38683     },
38684
38685     /**
38686      * Returns the editor defined for the cell/column.
38687      * return false or null to disable editing.
38688      * @param {Number} colIndex The column index
38689      * @param {Number} rowIndex The row index
38690      * @return {Object}
38691      */
38692     getCellEditor : function(colIndex, rowIndex){
38693         return this.config[colIndex].editor;
38694     },
38695
38696     /**
38697      * Sets if a column is editable.
38698      * @param {Number} col The column index
38699      * @param {Boolean} editable True if the column is editable
38700      */
38701     setEditable : function(col, editable){
38702         this.config[col].editable = editable;
38703     },
38704
38705
38706     /**
38707      * Returns true if the column is hidden.
38708      * @param {Number} colIndex The column index
38709      * @return {Boolean}
38710      */
38711     isHidden : function(colIndex){
38712         return this.config[colIndex].hidden;
38713     },
38714
38715
38716     /**
38717      * Returns true if the column width cannot be changed
38718      */
38719     isFixed : function(colIndex){
38720         return this.config[colIndex].fixed;
38721     },
38722
38723     /**
38724      * Returns true if the column can be resized
38725      * @return {Boolean}
38726      */
38727     isResizable : function(colIndex){
38728         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38729     },
38730     /**
38731      * Sets if a column is hidden.
38732      * @param {Number} colIndex The column index
38733      * @param {Boolean} hidden True if the column is hidden
38734      */
38735     setHidden : function(colIndex, hidden){
38736         this.config[colIndex].hidden = hidden;
38737         this.totalWidth = null;
38738         this.fireEvent("hiddenchange", this, colIndex, hidden);
38739     },
38740
38741     /**
38742      * Sets the editor for a column.
38743      * @param {Number} col The column index
38744      * @param {Object} editor The editor object
38745      */
38746     setEditor : function(col, editor){
38747         this.config[col].editor = editor;
38748     }
38749 });
38750
38751 Roo.grid.ColumnModel.defaultRenderer = function(value){
38752         if(typeof value == "string" && value.length < 1){
38753             return "&#160;";
38754         }
38755         return value;
38756 };
38757
38758 // Alias for backwards compatibility
38759 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38760 /*
38761  * Based on:
38762  * Ext JS Library 1.1.1
38763  * Copyright(c) 2006-2007, Ext JS, LLC.
38764  *
38765  * Originally Released Under LGPL - original licence link has changed is not relivant.
38766  *
38767  * Fork - LGPL
38768  * <script type="text/javascript">
38769  */
38770
38771 /**
38772  * @class Roo.grid.AbstractSelectionModel
38773  * @extends Roo.util.Observable
38774  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38775  * implemented by descendant classes.  This class should not be directly instantiated.
38776  * @constructor
38777  */
38778 Roo.grid.AbstractSelectionModel = function(){
38779     this.locked = false;
38780     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38781 };
38782
38783 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
38784     /** @ignore Called by the grid automatically. Do not call directly. */
38785     init : function(grid){
38786         this.grid = grid;
38787         this.initEvents();
38788     },
38789
38790     /**
38791      * Locks the selections.
38792      */
38793     lock : function(){
38794         this.locked = true;
38795     },
38796
38797     /**
38798      * Unlocks the selections.
38799      */
38800     unlock : function(){
38801         this.locked = false;
38802     },
38803
38804     /**
38805      * Returns true if the selections are locked.
38806      * @return {Boolean}
38807      */
38808     isLocked : function(){
38809         return this.locked;
38810     }
38811 });/*
38812  * Based on:
38813  * Ext JS Library 1.1.1
38814  * Copyright(c) 2006-2007, Ext JS, LLC.
38815  *
38816  * Originally Released Under LGPL - original licence link has changed is not relivant.
38817  *
38818  * Fork - LGPL
38819  * <script type="text/javascript">
38820  */
38821 /**
38822  * @extends Roo.grid.AbstractSelectionModel
38823  * @class Roo.grid.RowSelectionModel
38824  * The default SelectionModel used by {@link Roo.grid.Grid}.
38825  * It supports multiple selections and keyboard selection/navigation. 
38826  * @constructor
38827  * @param {Object} config
38828  */
38829 Roo.grid.RowSelectionModel = function(config){
38830     Roo.apply(this, config);
38831     this.selections = new Roo.util.MixedCollection(false, function(o){
38832         return o.id;
38833     });
38834
38835     this.last = false;
38836     this.lastActive = false;
38837
38838     this.addEvents({
38839         /**
38840              * @event selectionchange
38841              * Fires when the selection changes
38842              * @param {SelectionModel} this
38843              */
38844             "selectionchange" : true,
38845         /**
38846              * @event afterselectionchange
38847              * Fires after the selection changes (eg. by key press or clicking)
38848              * @param {SelectionModel} this
38849              */
38850             "afterselectionchange" : true,
38851         /**
38852              * @event beforerowselect
38853              * Fires when a row is selected being selected, return false to cancel.
38854              * @param {SelectionModel} this
38855              * @param {Number} rowIndex The selected index
38856              * @param {Boolean} keepExisting False if other selections will be cleared
38857              */
38858             "beforerowselect" : true,
38859         /**
38860              * @event rowselect
38861              * Fires when a row is selected.
38862              * @param {SelectionModel} this
38863              * @param {Number} rowIndex The selected index
38864              * @param {Roo.data.Record} r The record
38865              */
38866             "rowselect" : true,
38867         /**
38868              * @event rowdeselect
38869              * Fires when a row is deselected.
38870              * @param {SelectionModel} this
38871              * @param {Number} rowIndex The selected index
38872              */
38873         "rowdeselect" : true
38874     });
38875     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
38876     this.locked = false;
38877 };
38878
38879 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
38880     /**
38881      * @cfg {Boolean} singleSelect
38882      * True to allow selection of only one row at a time (defaults to false)
38883      */
38884     singleSelect : false,
38885
38886     // private
38887     initEvents : function(){
38888
38889         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
38890             this.grid.on("mousedown", this.handleMouseDown, this);
38891         }else{ // allow click to work like normal
38892             this.grid.on("rowclick", this.handleDragableRowClick, this);
38893         }
38894
38895         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
38896             "up" : function(e){
38897                 if(!e.shiftKey){
38898                     this.selectPrevious(e.shiftKey);
38899                 }else if(this.last !== false && this.lastActive !== false){
38900                     var last = this.last;
38901                     this.selectRange(this.last,  this.lastActive-1);
38902                     this.grid.getView().focusRow(this.lastActive);
38903                     if(last !== false){
38904                         this.last = last;
38905                     }
38906                 }else{
38907                     this.selectFirstRow();
38908                 }
38909                 this.fireEvent("afterselectionchange", this);
38910             },
38911             "down" : function(e){
38912                 if(!e.shiftKey){
38913                     this.selectNext(e.shiftKey);
38914                 }else if(this.last !== false && this.lastActive !== false){
38915                     var last = this.last;
38916                     this.selectRange(this.last,  this.lastActive+1);
38917                     this.grid.getView().focusRow(this.lastActive);
38918                     if(last !== false){
38919                         this.last = last;
38920                     }
38921                 }else{
38922                     this.selectFirstRow();
38923                 }
38924                 this.fireEvent("afterselectionchange", this);
38925             },
38926             scope: this
38927         });
38928
38929         var view = this.grid.view;
38930         view.on("refresh", this.onRefresh, this);
38931         view.on("rowupdated", this.onRowUpdated, this);
38932         view.on("rowremoved", this.onRemove, this);
38933     },
38934
38935     // private
38936     onRefresh : function(){
38937         var ds = this.grid.dataSource, i, v = this.grid.view;
38938         var s = this.selections;
38939         s.each(function(r){
38940             if((i = ds.indexOfId(r.id)) != -1){
38941                 v.onRowSelect(i);
38942             }else{
38943                 s.remove(r);
38944             }
38945         });
38946     },
38947
38948     // private
38949     onRemove : function(v, index, r){
38950         this.selections.remove(r);
38951     },
38952
38953     // private
38954     onRowUpdated : function(v, index, r){
38955         if(this.isSelected(r)){
38956             v.onRowSelect(index);
38957         }
38958     },
38959
38960     /**
38961      * Select records.
38962      * @param {Array} records The records to select
38963      * @param {Boolean} keepExisting (optional) True to keep existing selections
38964      */
38965     selectRecords : function(records, keepExisting){
38966         if(!keepExisting){
38967             this.clearSelections();
38968         }
38969         var ds = this.grid.dataSource;
38970         for(var i = 0, len = records.length; i < len; i++){
38971             this.selectRow(ds.indexOf(records[i]), true);
38972         }
38973     },
38974
38975     /**
38976      * Gets the number of selected rows.
38977      * @return {Number}
38978      */
38979     getCount : function(){
38980         return this.selections.length;
38981     },
38982
38983     /**
38984      * Selects the first row in the grid.
38985      */
38986     selectFirstRow : function(){
38987         this.selectRow(0);
38988     },
38989
38990     /**
38991      * Select the last row.
38992      * @param {Boolean} keepExisting (optional) True to keep existing selections
38993      */
38994     selectLastRow : function(keepExisting){
38995         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
38996     },
38997
38998     /**
38999      * Selects the row immediately following the last selected row.
39000      * @param {Boolean} keepExisting (optional) True to keep existing selections
39001      */
39002     selectNext : function(keepExisting){
39003         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39004             this.selectRow(this.last+1, keepExisting);
39005             this.grid.getView().focusRow(this.last);
39006         }
39007     },
39008
39009     /**
39010      * Selects the row that precedes the last selected row.
39011      * @param {Boolean} keepExisting (optional) True to keep existing selections
39012      */
39013     selectPrevious : function(keepExisting){
39014         if(this.last){
39015             this.selectRow(this.last-1, keepExisting);
39016             this.grid.getView().focusRow(this.last);
39017         }
39018     },
39019
39020     /**
39021      * Returns the selected records
39022      * @return {Array} Array of selected records
39023      */
39024     getSelections : function(){
39025         return [].concat(this.selections.items);
39026     },
39027
39028     /**
39029      * Returns the first selected record.
39030      * @return {Record}
39031      */
39032     getSelected : function(){
39033         return this.selections.itemAt(0);
39034     },
39035
39036
39037     /**
39038      * Clears all selections.
39039      */
39040     clearSelections : function(fast){
39041         if(this.locked) return;
39042         if(fast !== true){
39043             var ds = this.grid.dataSource;
39044             var s = this.selections;
39045             s.each(function(r){
39046                 this.deselectRow(ds.indexOfId(r.id));
39047             }, this);
39048             s.clear();
39049         }else{
39050             this.selections.clear();
39051         }
39052         this.last = false;
39053     },
39054
39055
39056     /**
39057      * Selects all rows.
39058      */
39059     selectAll : function(){
39060         if(this.locked) return;
39061         this.selections.clear();
39062         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39063             this.selectRow(i, true);
39064         }
39065     },
39066
39067     /**
39068      * Returns True if there is a selection.
39069      * @return {Boolean}
39070      */
39071     hasSelection : function(){
39072         return this.selections.length > 0;
39073     },
39074
39075     /**
39076      * Returns True if the specified row is selected.
39077      * @param {Number/Record} record The record or index of the record to check
39078      * @return {Boolean}
39079      */
39080     isSelected : function(index){
39081         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39082         return (r && this.selections.key(r.id) ? true : false);
39083     },
39084
39085     /**
39086      * Returns True if the specified record id is selected.
39087      * @param {String} id The id of record to check
39088      * @return {Boolean}
39089      */
39090     isIdSelected : function(id){
39091         return (this.selections.key(id) ? true : false);
39092     },
39093
39094     // private
39095     handleMouseDown : function(e, t){
39096         var view = this.grid.getView(), rowIndex;
39097         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39098             return;
39099         };
39100         if(e.shiftKey && this.last !== false){
39101             var last = this.last;
39102             this.selectRange(last, rowIndex, e.ctrlKey);
39103             this.last = last; // reset the last
39104             view.focusRow(rowIndex);
39105         }else{
39106             var isSelected = this.isSelected(rowIndex);
39107             if(e.button !== 0 && isSelected){
39108                 view.focusRow(rowIndex);
39109             }else if(e.ctrlKey && isSelected){
39110                 this.deselectRow(rowIndex);
39111             }else if(!isSelected){
39112                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39113                 view.focusRow(rowIndex);
39114             }
39115         }
39116         this.fireEvent("afterselectionchange", this);
39117     },
39118     // private
39119     handleDragableRowClick :  function(grid, rowIndex, e) 
39120     {
39121         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39122             this.selectRow(rowIndex, false);
39123             grid.view.focusRow(rowIndex);
39124              this.fireEvent("afterselectionchange", this);
39125         }
39126     },
39127     
39128     /**
39129      * Selects multiple rows.
39130      * @param {Array} rows Array of the indexes of the row to select
39131      * @param {Boolean} keepExisting (optional) True to keep existing selections
39132      */
39133     selectRows : function(rows, keepExisting){
39134         if(!keepExisting){
39135             this.clearSelections();
39136         }
39137         for(var i = 0, len = rows.length; i < len; i++){
39138             this.selectRow(rows[i], true);
39139         }
39140     },
39141
39142     /**
39143      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39144      * @param {Number} startRow The index of the first row in the range
39145      * @param {Number} endRow The index of the last row in the range
39146      * @param {Boolean} keepExisting (optional) True to retain existing selections
39147      */
39148     selectRange : function(startRow, endRow, keepExisting){
39149         if(this.locked) return;
39150         if(!keepExisting){
39151             this.clearSelections();
39152         }
39153         if(startRow <= endRow){
39154             for(var i = startRow; i <= endRow; i++){
39155                 this.selectRow(i, true);
39156             }
39157         }else{
39158             for(var i = startRow; i >= endRow; i--){
39159                 this.selectRow(i, true);
39160             }
39161         }
39162     },
39163
39164     /**
39165      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39166      * @param {Number} startRow The index of the first row in the range
39167      * @param {Number} endRow The index of the last row in the range
39168      */
39169     deselectRange : function(startRow, endRow, preventViewNotify){
39170         if(this.locked) return;
39171         for(var i = startRow; i <= endRow; i++){
39172             this.deselectRow(i, preventViewNotify);
39173         }
39174     },
39175
39176     /**
39177      * Selects a row.
39178      * @param {Number} row The index of the row to select
39179      * @param {Boolean} keepExisting (optional) True to keep existing selections
39180      */
39181     selectRow : function(index, keepExisting, preventViewNotify){
39182         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39183         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39184             if(!keepExisting || this.singleSelect){
39185                 this.clearSelections();
39186             }
39187             var r = this.grid.dataSource.getAt(index);
39188             this.selections.add(r);
39189             this.last = this.lastActive = index;
39190             if(!preventViewNotify){
39191                 this.grid.getView().onRowSelect(index);
39192             }
39193             this.fireEvent("rowselect", this, index, r);
39194             this.fireEvent("selectionchange", this);
39195         }
39196     },
39197
39198     /**
39199      * Deselects a row.
39200      * @param {Number} row The index of the row to deselect
39201      */
39202     deselectRow : function(index, preventViewNotify){
39203         if(this.locked) return;
39204         if(this.last == index){
39205             this.last = false;
39206         }
39207         if(this.lastActive == index){
39208             this.lastActive = false;
39209         }
39210         var r = this.grid.dataSource.getAt(index);
39211         this.selections.remove(r);
39212         if(!preventViewNotify){
39213             this.grid.getView().onRowDeselect(index);
39214         }
39215         this.fireEvent("rowdeselect", this, index);
39216         this.fireEvent("selectionchange", this);
39217     },
39218
39219     // private
39220     restoreLast : function(){
39221         if(this._last){
39222             this.last = this._last;
39223         }
39224     },
39225
39226     // private
39227     acceptsNav : function(row, col, cm){
39228         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39229     },
39230
39231     // private
39232     onEditorKey : function(field, e){
39233         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39234         if(k == e.TAB){
39235             e.stopEvent();
39236             ed.completeEdit();
39237             if(e.shiftKey){
39238                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39239             }else{
39240                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39241             }
39242         }else if(k == e.ENTER && !e.ctrlKey){
39243             e.stopEvent();
39244             ed.completeEdit();
39245             if(e.shiftKey){
39246                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39247             }else{
39248                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39249             }
39250         }else if(k == e.ESC){
39251             ed.cancelEdit();
39252         }
39253         if(newCell){
39254             g.startEditing(newCell[0], newCell[1]);
39255         }
39256     }
39257 });/*
39258  * Based on:
39259  * Ext JS Library 1.1.1
39260  * Copyright(c) 2006-2007, Ext JS, LLC.
39261  *
39262  * Originally Released Under LGPL - original licence link has changed is not relivant.
39263  *
39264  * Fork - LGPL
39265  * <script type="text/javascript">
39266  */
39267 /**
39268  * @class Roo.grid.CellSelectionModel
39269  * @extends Roo.grid.AbstractSelectionModel
39270  * This class provides the basic implementation for cell selection in a grid.
39271  * @constructor
39272  * @param {Object} config The object containing the configuration of this model.
39273  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39274  */
39275 Roo.grid.CellSelectionModel = function(config){
39276     Roo.apply(this, config);
39277
39278     this.selection = null;
39279
39280     this.addEvents({
39281         /**
39282              * @event beforerowselect
39283              * Fires before a cell is selected.
39284              * @param {SelectionModel} this
39285              * @param {Number} rowIndex The selected row index
39286              * @param {Number} colIndex The selected cell index
39287              */
39288             "beforecellselect" : true,
39289         /**
39290              * @event cellselect
39291              * Fires when a cell is selected.
39292              * @param {SelectionModel} this
39293              * @param {Number} rowIndex The selected row index
39294              * @param {Number} colIndex The selected cell index
39295              */
39296             "cellselect" : true,
39297         /**
39298              * @event selectionchange
39299              * Fires when the active selection changes.
39300              * @param {SelectionModel} this
39301              * @param {Object} selection null for no selection or an object (o) with two properties
39302                 <ul>
39303                 <li>o.record: the record object for the row the selection is in</li>
39304                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39305                 </ul>
39306              */
39307             "selectionchange" : true,
39308         /**
39309              * @event tabend
39310              * Fires when the tab (or enter) was pressed on the last editable cell
39311              * You can use this to trigger add new row.
39312              * @param {SelectionModel} this
39313              */
39314             "tabend" : true,
39315          /**
39316              * @event beforeeditnext
39317              * Fires before the next editable sell is made active
39318              * You can use this to skip to another cell or fire the tabend
39319              *    if you set cell to false
39320              * @param {Object} eventdata object : { cell : [ row, col ] } 
39321              */
39322             "beforeeditnext" : true
39323     });
39324     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39325 };
39326
39327 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39328     
39329     enter_is_tab: false,
39330
39331     /** @ignore */
39332     initEvents : function(){
39333         this.grid.on("mousedown", this.handleMouseDown, this);
39334         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39335         var view = this.grid.view;
39336         view.on("refresh", this.onViewChange, this);
39337         view.on("rowupdated", this.onRowUpdated, this);
39338         view.on("beforerowremoved", this.clearSelections, this);
39339         view.on("beforerowsinserted", this.clearSelections, this);
39340         if(this.grid.isEditor){
39341             this.grid.on("beforeedit", this.beforeEdit,  this);
39342         }
39343     },
39344
39345         //private
39346     beforeEdit : function(e){
39347         this.select(e.row, e.column, false, true, e.record);
39348     },
39349
39350         //private
39351     onRowUpdated : function(v, index, r){
39352         if(this.selection && this.selection.record == r){
39353             v.onCellSelect(index, this.selection.cell[1]);
39354         }
39355     },
39356
39357         //private
39358     onViewChange : function(){
39359         this.clearSelections(true);
39360     },
39361
39362         /**
39363          * Returns the currently selected cell,.
39364          * @return {Array} The selected cell (row, column) or null if none selected.
39365          */
39366     getSelectedCell : function(){
39367         return this.selection ? this.selection.cell : null;
39368     },
39369
39370     /**
39371      * Clears all selections.
39372      * @param {Boolean} true to prevent the gridview from being notified about the change.
39373      */
39374     clearSelections : function(preventNotify){
39375         var s = this.selection;
39376         if(s){
39377             if(preventNotify !== true){
39378                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39379             }
39380             this.selection = null;
39381             this.fireEvent("selectionchange", this, null);
39382         }
39383     },
39384
39385     /**
39386      * Returns true if there is a selection.
39387      * @return {Boolean}
39388      */
39389     hasSelection : function(){
39390         return this.selection ? true : false;
39391     },
39392
39393     /** @ignore */
39394     handleMouseDown : function(e, t){
39395         var v = this.grid.getView();
39396         if(this.isLocked()){
39397             return;
39398         };
39399         var row = v.findRowIndex(t);
39400         var cell = v.findCellIndex(t);
39401         if(row !== false && cell !== false){
39402             this.select(row, cell);
39403         }
39404     },
39405
39406     /**
39407      * Selects a cell.
39408      * @param {Number} rowIndex
39409      * @param {Number} collIndex
39410      */
39411     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39412         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39413             this.clearSelections();
39414             r = r || this.grid.dataSource.getAt(rowIndex);
39415             this.selection = {
39416                 record : r,
39417                 cell : [rowIndex, colIndex]
39418             };
39419             if(!preventViewNotify){
39420                 var v = this.grid.getView();
39421                 v.onCellSelect(rowIndex, colIndex);
39422                 if(preventFocus !== true){
39423                     v.focusCell(rowIndex, colIndex);
39424                 }
39425             }
39426             this.fireEvent("cellselect", this, rowIndex, colIndex);
39427             this.fireEvent("selectionchange", this, this.selection);
39428         }
39429     },
39430
39431         //private
39432     isSelectable : function(rowIndex, colIndex, cm){
39433         return !cm.isHidden(colIndex);
39434     },
39435
39436     /** @ignore */
39437     handleKeyDown : function(e){
39438         //Roo.log('Cell Sel Model handleKeyDown');
39439         if(!e.isNavKeyPress()){
39440             return;
39441         }
39442         var g = this.grid, s = this.selection;
39443         if(!s){
39444             e.stopEvent();
39445             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39446             if(cell){
39447                 this.select(cell[0], cell[1]);
39448             }
39449             return;
39450         }
39451         var sm = this;
39452         var walk = function(row, col, step){
39453             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39454         };
39455         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39456         var newCell;
39457
39458       
39459
39460         switch(k){
39461             case e.TAB:
39462                 // handled by onEditorKey
39463                 if (g.isEditor && g.editing) {
39464                     return;
39465                 }
39466                 if(e.shiftKey) {
39467                     newCell = walk(r, c-1, -1);
39468                 } else {
39469                     newCell = walk(r, c+1, 1);
39470                 }
39471                 break;
39472             
39473             case e.DOWN:
39474                newCell = walk(r+1, c, 1);
39475                 break;
39476             
39477             case e.UP:
39478                 newCell = walk(r-1, c, -1);
39479                 break;
39480             
39481             case e.RIGHT:
39482                 newCell = walk(r, c+1, 1);
39483                 break;
39484             
39485             case e.LEFT:
39486                 newCell = walk(r, c-1, -1);
39487                 break;
39488             
39489             case e.ENTER:
39490                 
39491                 if(g.isEditor && !g.editing){
39492                    g.startEditing(r, c);
39493                    e.stopEvent();
39494                    return;
39495                 }
39496                 
39497                 
39498              break;
39499         };
39500         if(newCell){
39501             this.select(newCell[0], newCell[1]);
39502             e.stopEvent();
39503             
39504         }
39505     },
39506
39507     acceptsNav : function(row, col, cm){
39508         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39509     },
39510     /**
39511      * Selects a cell.
39512      * @param {Number} field (not used) - as it's normally used as a listener
39513      * @param {Number} e - event - fake it by using
39514      *
39515      * var e = Roo.EventObjectImpl.prototype;
39516      * e.keyCode = e.TAB
39517      *
39518      * 
39519      */
39520     onEditorKey : function(field, e){
39521         
39522         var k = e.getKey(),
39523             newCell,
39524             g = this.grid,
39525             ed = g.activeEditor,
39526             forward = false;
39527         ///Roo.log('onEditorKey' + k);
39528         
39529         
39530         if (this.enter_is_tab && k == e.ENTER) {
39531             k = e.TAB;
39532         }
39533         
39534         if(k == e.TAB){
39535             if(e.shiftKey){
39536                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39537             }else{
39538                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39539                 forward = true;
39540             }
39541             
39542             e.stopEvent();
39543             
39544         } else if(k == e.ENTER &&  !e.ctrlKey){
39545             ed.completeEdit();
39546             e.stopEvent();
39547             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39548         
39549                 } else if(k == e.ESC){
39550             ed.cancelEdit();
39551         }
39552                 
39553         if (newCell) {
39554             var ecall = { cell : newCell, forward : forward };
39555             this.fireEvent('beforeeditnext', ecall );
39556             newCell = ecall.cell;
39557                         forward = ecall.forward;
39558         }
39559                 
39560         if(newCell){
39561             //Roo.log('next cell after edit');
39562             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39563         } else if (forward) {
39564             // tabbed past last
39565             this.fireEvent.defer(100, this, ['tabend',this]);
39566         }
39567     }
39568 });/*
39569  * Based on:
39570  * Ext JS Library 1.1.1
39571  * Copyright(c) 2006-2007, Ext JS, LLC.
39572  *
39573  * Originally Released Under LGPL - original licence link has changed is not relivant.
39574  *
39575  * Fork - LGPL
39576  * <script type="text/javascript">
39577  */
39578  
39579 /**
39580  * @class Roo.grid.EditorGrid
39581  * @extends Roo.grid.Grid
39582  * Class for creating and editable grid.
39583  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
39584  * The container MUST have some type of size defined for the grid to fill. The container will be 
39585  * automatically set to position relative if it isn't already.
39586  * @param {Object} dataSource The data model to bind to
39587  * @param {Object} colModel The column model with info about this grid's columns
39588  */
39589 Roo.grid.EditorGrid = function(container, config){
39590     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39591     this.getGridEl().addClass("xedit-grid");
39592
39593     if(!this.selModel){
39594         this.selModel = new Roo.grid.CellSelectionModel();
39595     }
39596
39597     this.activeEditor = null;
39598
39599         this.addEvents({
39600             /**
39601              * @event beforeedit
39602              * Fires before cell editing is triggered. The edit event object has the following properties <br />
39603              * <ul style="padding:5px;padding-left:16px;">
39604              * <li>grid - This grid</li>
39605              * <li>record - The record being edited</li>
39606              * <li>field - The field name being edited</li>
39607              * <li>value - The value for the field being edited.</li>
39608              * <li>row - The grid row index</li>
39609              * <li>column - The grid column index</li>
39610              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39611              * </ul>
39612              * @param {Object} e An edit event (see above for description)
39613              */
39614             "beforeedit" : true,
39615             /**
39616              * @event afteredit
39617              * Fires after a cell is edited. <br />
39618              * <ul style="padding:5px;padding-left:16px;">
39619              * <li>grid - This grid</li>
39620              * <li>record - The record being edited</li>
39621              * <li>field - The field name being edited</li>
39622              * <li>value - The value being set</li>
39623              * <li>originalValue - The original value for the field, before the edit.</li>
39624              * <li>row - The grid row index</li>
39625              * <li>column - The grid column index</li>
39626              * </ul>
39627              * @param {Object} e An edit event (see above for description)
39628              */
39629             "afteredit" : true,
39630             /**
39631              * @event validateedit
39632              * Fires after a cell is edited, but before the value is set in the record. 
39633          * You can use this to modify the value being set in the field, Return false
39634              * to cancel the change. The edit event object has the following properties <br />
39635              * <ul style="padding:5px;padding-left:16px;">
39636          * <li>editor - This editor</li>
39637              * <li>grid - This grid</li>
39638              * <li>record - The record being edited</li>
39639              * <li>field - The field name being edited</li>
39640              * <li>value - The value being set</li>
39641              * <li>originalValue - The original value for the field, before the edit.</li>
39642              * <li>row - The grid row index</li>
39643              * <li>column - The grid column index</li>
39644              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39645              * </ul>
39646              * @param {Object} e An edit event (see above for description)
39647              */
39648             "validateedit" : true
39649         });
39650     this.on("bodyscroll", this.stopEditing,  this);
39651     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
39652 };
39653
39654 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39655     /**
39656      * @cfg {Number} clicksToEdit
39657      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39658      */
39659     clicksToEdit: 2,
39660
39661     // private
39662     isEditor : true,
39663     // private
39664     trackMouseOver: false, // causes very odd FF errors
39665
39666     onCellDblClick : function(g, row, col){
39667         this.startEditing(row, col);
39668     },
39669
39670     onEditComplete : function(ed, value, startValue){
39671         this.editing = false;
39672         this.activeEditor = null;
39673         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39674         var r = ed.record;
39675         var field = this.colModel.getDataIndex(ed.col);
39676         var e = {
39677             grid: this,
39678             record: r,
39679             field: field,
39680             originalValue: startValue,
39681             value: value,
39682             row: ed.row,
39683             column: ed.col,
39684             cancel:false,
39685             editor: ed
39686         };
39687         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39688         cell.show();
39689           
39690         if(String(value) !== String(startValue)){
39691             
39692             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39693                 r.set(field, e.value);
39694                 // if we are dealing with a combo box..
39695                 // then we also set the 'name' colum to be the displayField
39696                 if (ed.field.displayField && ed.field.name) {
39697                     r.set(ed.field.name, ed.field.el.dom.value);
39698                 }
39699                 
39700                 delete e.cancel; //?? why!!!
39701                 this.fireEvent("afteredit", e);
39702             }
39703         } else {
39704             this.fireEvent("afteredit", e); // always fire it!
39705         }
39706         this.view.focusCell(ed.row, ed.col);
39707     },
39708
39709     /**
39710      * Starts editing the specified for the specified row/column
39711      * @param {Number} rowIndex
39712      * @param {Number} colIndex
39713      */
39714     startEditing : function(row, col){
39715         this.stopEditing();
39716         if(this.colModel.isCellEditable(col, row)){
39717             this.view.ensureVisible(row, col, true);
39718           
39719             var r = this.dataSource.getAt(row);
39720             var field = this.colModel.getDataIndex(col);
39721             var cell = Roo.get(this.view.getCell(row,col));
39722             var e = {
39723                 grid: this,
39724                 record: r,
39725                 field: field,
39726                 value: r.data[field],
39727                 row: row,
39728                 column: col,
39729                 cancel:false 
39730             };
39731             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39732                 this.editing = true;
39733                 var ed = this.colModel.getCellEditor(col, row);
39734                 
39735                 if (!ed) {
39736                     return;
39737                 }
39738                 if(!ed.rendered){
39739                     ed.render(ed.parentEl || document.body);
39740                 }
39741                 ed.field.reset();
39742                
39743                 cell.hide();
39744                 
39745                 (function(){ // complex but required for focus issues in safari, ie and opera
39746                     ed.row = row;
39747                     ed.col = col;
39748                     ed.record = r;
39749                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
39750                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39751                     this.activeEditor = ed;
39752                     var v = r.data[field];
39753                     ed.startEdit(this.view.getCell(row, col), v);
39754                     // combo's with 'displayField and name set
39755                     if (ed.field.displayField && ed.field.name) {
39756                         ed.field.el.dom.value = r.data[ed.field.name];
39757                     }
39758                     
39759                     
39760                 }).defer(50, this);
39761             }
39762         }
39763     },
39764         
39765     /**
39766      * Stops any active editing
39767      */
39768     stopEditing : function(){
39769         if(this.activeEditor){
39770             this.activeEditor.completeEdit();
39771         }
39772         this.activeEditor = null;
39773     },
39774         
39775          /**
39776      * Called to get grid's drag proxy text, by default returns this.ddText.
39777      * @return {String}
39778      */
39779     getDragDropText : function(){
39780         var count = this.selModel.getSelectedCell() ? 1 : 0;
39781         return String.format(this.ddText, count, count == 1 ? '' : 's');
39782     }
39783         
39784 });/*
39785  * Based on:
39786  * Ext JS Library 1.1.1
39787  * Copyright(c) 2006-2007, Ext JS, LLC.
39788  *
39789  * Originally Released Under LGPL - original licence link has changed is not relivant.
39790  *
39791  * Fork - LGPL
39792  * <script type="text/javascript">
39793  */
39794
39795 // private - not really -- you end up using it !
39796 // This is a support class used internally by the Grid components
39797
39798 /**
39799  * @class Roo.grid.GridEditor
39800  * @extends Roo.Editor
39801  * Class for creating and editable grid elements.
39802  * @param {Object} config any settings (must include field)
39803  */
39804 Roo.grid.GridEditor = function(field, config){
39805     if (!config && field.field) {
39806         config = field;
39807         field = Roo.factory(config.field, Roo.form);
39808     }
39809     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
39810     field.monitorTab = false;
39811 };
39812
39813 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
39814     
39815     /**
39816      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
39817      */
39818     
39819     alignment: "tl-tl",
39820     autoSize: "width",
39821     hideEl : false,
39822     cls: "x-small-editor x-grid-editor",
39823     shim:false,
39824     shadow:"frame"
39825 });/*
39826  * Based on:
39827  * Ext JS Library 1.1.1
39828  * Copyright(c) 2006-2007, Ext JS, LLC.
39829  *
39830  * Originally Released Under LGPL - original licence link has changed is not relivant.
39831  *
39832  * Fork - LGPL
39833  * <script type="text/javascript">
39834  */
39835   
39836
39837   
39838 Roo.grid.PropertyRecord = Roo.data.Record.create([
39839     {name:'name',type:'string'},  'value'
39840 ]);
39841
39842
39843 Roo.grid.PropertyStore = function(grid, source){
39844     this.grid = grid;
39845     this.store = new Roo.data.Store({
39846         recordType : Roo.grid.PropertyRecord
39847     });
39848     this.store.on('update', this.onUpdate,  this);
39849     if(source){
39850         this.setSource(source);
39851     }
39852     Roo.grid.PropertyStore.superclass.constructor.call(this);
39853 };
39854
39855
39856
39857 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
39858     setSource : function(o){
39859         this.source = o;
39860         this.store.removeAll();
39861         var data = [];
39862         for(var k in o){
39863             if(this.isEditableValue(o[k])){
39864                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
39865             }
39866         }
39867         this.store.loadRecords({records: data}, {}, true);
39868     },
39869
39870     onUpdate : function(ds, record, type){
39871         if(type == Roo.data.Record.EDIT){
39872             var v = record.data['value'];
39873             var oldValue = record.modified['value'];
39874             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
39875                 this.source[record.id] = v;
39876                 record.commit();
39877                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
39878             }else{
39879                 record.reject();
39880             }
39881         }
39882     },
39883
39884     getProperty : function(row){
39885        return this.store.getAt(row);
39886     },
39887
39888     isEditableValue: function(val){
39889         if(val && val instanceof Date){
39890             return true;
39891         }else if(typeof val == 'object' || typeof val == 'function'){
39892             return false;
39893         }
39894         return true;
39895     },
39896
39897     setValue : function(prop, value){
39898         this.source[prop] = value;
39899         this.store.getById(prop).set('value', value);
39900     },
39901
39902     getSource : function(){
39903         return this.source;
39904     }
39905 });
39906
39907 Roo.grid.PropertyColumnModel = function(grid, store){
39908     this.grid = grid;
39909     var g = Roo.grid;
39910     g.PropertyColumnModel.superclass.constructor.call(this, [
39911         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
39912         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
39913     ]);
39914     this.store = store;
39915     this.bselect = Roo.DomHelper.append(document.body, {
39916         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
39917             {tag: 'option', value: 'true', html: 'true'},
39918             {tag: 'option', value: 'false', html: 'false'}
39919         ]
39920     });
39921     Roo.id(this.bselect);
39922     var f = Roo.form;
39923     this.editors = {
39924         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
39925         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
39926         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
39927         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
39928         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
39929     };
39930     this.renderCellDelegate = this.renderCell.createDelegate(this);
39931     this.renderPropDelegate = this.renderProp.createDelegate(this);
39932 };
39933
39934 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
39935     
39936     
39937     nameText : 'Name',
39938     valueText : 'Value',
39939     
39940     dateFormat : 'm/j/Y',
39941     
39942     
39943     renderDate : function(dateVal){
39944         return dateVal.dateFormat(this.dateFormat);
39945     },
39946
39947     renderBool : function(bVal){
39948         return bVal ? 'true' : 'false';
39949     },
39950
39951     isCellEditable : function(colIndex, rowIndex){
39952         return colIndex == 1;
39953     },
39954
39955     getRenderer : function(col){
39956         return col == 1 ?
39957             this.renderCellDelegate : this.renderPropDelegate;
39958     },
39959
39960     renderProp : function(v){
39961         return this.getPropertyName(v);
39962     },
39963
39964     renderCell : function(val){
39965         var rv = val;
39966         if(val instanceof Date){
39967             rv = this.renderDate(val);
39968         }else if(typeof val == 'boolean'){
39969             rv = this.renderBool(val);
39970         }
39971         return Roo.util.Format.htmlEncode(rv);
39972     },
39973
39974     getPropertyName : function(name){
39975         var pn = this.grid.propertyNames;
39976         return pn && pn[name] ? pn[name] : name;
39977     },
39978
39979     getCellEditor : function(colIndex, rowIndex){
39980         var p = this.store.getProperty(rowIndex);
39981         var n = p.data['name'], val = p.data['value'];
39982         
39983         if(typeof(this.grid.customEditors[n]) == 'string'){
39984             return this.editors[this.grid.customEditors[n]];
39985         }
39986         if(typeof(this.grid.customEditors[n]) != 'undefined'){
39987             return this.grid.customEditors[n];
39988         }
39989         if(val instanceof Date){
39990             return this.editors['date'];
39991         }else if(typeof val == 'number'){
39992             return this.editors['number'];
39993         }else if(typeof val == 'boolean'){
39994             return this.editors['boolean'];
39995         }else{
39996             return this.editors['string'];
39997         }
39998     }
39999 });
40000
40001 /**
40002  * @class Roo.grid.PropertyGrid
40003  * @extends Roo.grid.EditorGrid
40004  * This class represents the  interface of a component based property grid control.
40005  * <br><br>Usage:<pre><code>
40006  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40007       
40008  });
40009  // set any options
40010  grid.render();
40011  * </code></pre>
40012   
40013  * @constructor
40014  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40015  * The container MUST have some type of size defined for the grid to fill. The container will be
40016  * automatically set to position relative if it isn't already.
40017  * @param {Object} config A config object that sets properties on this grid.
40018  */
40019 Roo.grid.PropertyGrid = function(container, config){
40020     config = config || {};
40021     var store = new Roo.grid.PropertyStore(this);
40022     this.store = store;
40023     var cm = new Roo.grid.PropertyColumnModel(this, store);
40024     store.store.sort('name', 'ASC');
40025     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40026         ds: store.store,
40027         cm: cm,
40028         enableColLock:false,
40029         enableColumnMove:false,
40030         stripeRows:false,
40031         trackMouseOver: false,
40032         clicksToEdit:1
40033     }, config));
40034     this.getGridEl().addClass('x-props-grid');
40035     this.lastEditRow = null;
40036     this.on('columnresize', this.onColumnResize, this);
40037     this.addEvents({
40038          /**
40039              * @event beforepropertychange
40040              * Fires before a property changes (return false to stop?)
40041              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40042              * @param {String} id Record Id
40043              * @param {String} newval New Value
40044          * @param {String} oldval Old Value
40045              */
40046         "beforepropertychange": true,
40047         /**
40048              * @event propertychange
40049              * Fires after a property changes
40050              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40051              * @param {String} id Record Id
40052              * @param {String} newval New Value
40053          * @param {String} oldval Old Value
40054              */
40055         "propertychange": true
40056     });
40057     this.customEditors = this.customEditors || {};
40058 };
40059 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40060     
40061      /**
40062      * @cfg {Object} customEditors map of colnames=> custom editors.
40063      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40064      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40065      * false disables editing of the field.
40066          */
40067     
40068       /**
40069      * @cfg {Object} propertyNames map of property Names to their displayed value
40070          */
40071     
40072     render : function(){
40073         Roo.grid.PropertyGrid.superclass.render.call(this);
40074         this.autoSize.defer(100, this);
40075     },
40076
40077     autoSize : function(){
40078         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40079         if(this.view){
40080             this.view.fitColumns();
40081         }
40082     },
40083
40084     onColumnResize : function(){
40085         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40086         this.autoSize();
40087     },
40088     /**
40089      * Sets the data for the Grid
40090      * accepts a Key => Value object of all the elements avaiable.
40091      * @param {Object} data  to appear in grid.
40092      */
40093     setSource : function(source){
40094         this.store.setSource(source);
40095         //this.autoSize();
40096     },
40097     /**
40098      * Gets all the data from the grid.
40099      * @return {Object} data  data stored in grid
40100      */
40101     getSource : function(){
40102         return this.store.getSource();
40103     }
40104 });/*
40105  * Based on:
40106  * Ext JS Library 1.1.1
40107  * Copyright(c) 2006-2007, Ext JS, LLC.
40108  *
40109  * Originally Released Under LGPL - original licence link has changed is not relivant.
40110  *
40111  * Fork - LGPL
40112  * <script type="text/javascript">
40113  */
40114  
40115 /**
40116  * @class Roo.LoadMask
40117  * A simple utility class for generically masking elements while loading data.  If the element being masked has
40118  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
40119  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
40120  * element's UpdateManager load indicator and will be destroyed after the initial load.
40121  * @constructor
40122  * Create a new LoadMask
40123  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
40124  * @param {Object} config The config object
40125  */
40126 Roo.LoadMask = function(el, config){
40127     this.el = Roo.get(el);
40128     Roo.apply(this, config);
40129     if(this.store){
40130         this.store.on('beforeload', this.onBeforeLoad, this);
40131         this.store.on('load', this.onLoad, this);
40132         this.store.on('loadexception', this.onLoadException, this);
40133         this.removeMask = false;
40134     }else{
40135         var um = this.el.getUpdateManager();
40136         um.showLoadIndicator = false; // disable the default indicator
40137         um.on('beforeupdate', this.onBeforeLoad, this);
40138         um.on('update', this.onLoad, this);
40139         um.on('failure', this.onLoad, this);
40140         this.removeMask = true;
40141     }
40142 };
40143
40144 Roo.LoadMask.prototype = {
40145     /**
40146      * @cfg {Boolean} removeMask
40147      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
40148      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
40149      */
40150     /**
40151      * @cfg {String} msg
40152      * The text to display in a centered loading message box (defaults to 'Loading...')
40153      */
40154     msg : 'Loading...',
40155     /**
40156      * @cfg {String} msgCls
40157      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
40158      */
40159     msgCls : 'x-mask-loading',
40160
40161     /**
40162      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
40163      * @type Boolean
40164      */
40165     disabled: false,
40166
40167     /**
40168      * Disables the mask to prevent it from being displayed
40169      */
40170     disable : function(){
40171        this.disabled = true;
40172     },
40173
40174     /**
40175      * Enables the mask so that it can be displayed
40176      */
40177     enable : function(){
40178         this.disabled = false;
40179     },
40180     
40181     onLoadException : function()
40182     {
40183         Roo.log(arguments);
40184         
40185         if (typeof(arguments[3]) != 'undefined') {
40186             Roo.MessageBox.alert("Error loading",arguments[3]);
40187         } 
40188         /*
40189         try {
40190             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
40191                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
40192             }   
40193         } catch(e) {
40194             
40195         }
40196         */
40197     
40198         
40199         
40200         this.el.unmask(this.removeMask);
40201     },
40202     // private
40203     onLoad : function()
40204     {
40205         this.el.unmask(this.removeMask);
40206     },
40207
40208     // private
40209     onBeforeLoad : function(){
40210         if(!this.disabled){
40211             this.el.mask(this.msg, this.msgCls);
40212         }
40213     },
40214
40215     // private
40216     destroy : function(){
40217         if(this.store){
40218             this.store.un('beforeload', this.onBeforeLoad, this);
40219             this.store.un('load', this.onLoad, this);
40220             this.store.un('loadexception', this.onLoadException, this);
40221         }else{
40222             var um = this.el.getUpdateManager();
40223             um.un('beforeupdate', this.onBeforeLoad, this);
40224             um.un('update', this.onLoad, this);
40225             um.un('failure', this.onLoad, this);
40226         }
40227     }
40228 };/*
40229  * Based on:
40230  * Ext JS Library 1.1.1
40231  * Copyright(c) 2006-2007, Ext JS, LLC.
40232  *
40233  * Originally Released Under LGPL - original licence link has changed is not relivant.
40234  *
40235  * Fork - LGPL
40236  * <script type="text/javascript">
40237  */
40238
40239
40240 /**
40241  * @class Roo.XTemplate
40242  * @extends Roo.Template
40243  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
40244 <pre><code>
40245 var t = new Roo.XTemplate(
40246         '&lt;select name="{name}"&gt;',
40247                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
40248         '&lt;/select&gt;'
40249 );
40250  
40251 // then append, applying the master template values
40252  </code></pre>
40253  *
40254  * Supported features:
40255  *
40256  *  Tags:
40257
40258 <pre><code>
40259       {a_variable} - output encoded.
40260       {a_variable.format:("Y-m-d")} - call a method on the variable
40261       {a_variable:raw} - unencoded output
40262       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
40263       {a_variable:this.method_on_template(...)} - call a method on the template object.
40264  
40265 </code></pre>
40266  *  The tpl tag:
40267 <pre><code>
40268         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
40269         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
40270         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
40271         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
40272   
40273         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
40274         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
40275 </code></pre>
40276  *      
40277  */
40278 Roo.XTemplate = function()
40279 {
40280     Roo.XTemplate.superclass.constructor.apply(this, arguments);
40281     if (this.html) {
40282         this.compile();
40283     }
40284 };
40285
40286
40287 Roo.extend(Roo.XTemplate, Roo.Template, {
40288
40289     /**
40290      * The various sub templates
40291      */
40292     tpls : false,
40293     /**
40294      *
40295      * basic tag replacing syntax
40296      * WORD:WORD()
40297      *
40298      * // you can fake an object call by doing this
40299      *  x.t:(test,tesT) 
40300      * 
40301      */
40302     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
40303
40304     /**
40305      * compile the template
40306      *
40307      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
40308      *
40309      */
40310     compile: function()
40311     {
40312         var s = this.html;
40313      
40314         s = ['<tpl>', s, '</tpl>'].join('');
40315     
40316         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
40317             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
40318             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
40319             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
40320             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
40321             m,
40322             id     = 0,
40323             tpls   = [];
40324     
40325         while(true == !!(m = s.match(re))){
40326             var forMatch   = m[0].match(nameRe),
40327                 ifMatch   = m[0].match(ifRe),
40328                 execMatch   = m[0].match(execRe),
40329                 namedMatch   = m[0].match(namedRe),
40330                 
40331                 exp  = null, 
40332                 fn   = null,
40333                 exec = null,
40334                 name = forMatch && forMatch[1] ? forMatch[1] : '';
40335                 
40336             if (ifMatch) {
40337                 // if - puts fn into test..
40338                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
40339                 if(exp){
40340                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
40341                 }
40342             }
40343             
40344             if (execMatch) {
40345                 // exec - calls a function... returns empty if true is  returned.
40346                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
40347                 if(exp){
40348                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
40349                 }
40350             }
40351             
40352             
40353             if (name) {
40354                 // for = 
40355                 switch(name){
40356                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
40357                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
40358                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
40359                 }
40360             }
40361             var uid = namedMatch ? namedMatch[1] : id;
40362             
40363             
40364             tpls.push({
40365                 id:     namedMatch ? namedMatch[1] : id,
40366                 target: name,
40367                 exec:   exec,
40368                 test:   fn,
40369                 body:   m[1] || ''
40370             });
40371             if (namedMatch) {
40372                 s = s.replace(m[0], '');
40373             } else { 
40374                 s = s.replace(m[0], '{xtpl'+ id + '}');
40375             }
40376             ++id;
40377         }
40378         this.tpls = [];
40379         for(var i = tpls.length-1; i >= 0; --i){
40380             this.compileTpl(tpls[i]);
40381             this.tpls[tpls[i].id] = tpls[i];
40382         }
40383         this.master = tpls[tpls.length-1];
40384         return this;
40385     },
40386     /**
40387      * same as applyTemplate, except it's done to one of the subTemplates
40388      * when using named templates, you can do:
40389      *
40390      * var str = pl.applySubTemplate('your-name', values);
40391      *
40392      * 
40393      * @param {Number} id of the template
40394      * @param {Object} values to apply to template
40395      * @param {Object} parent (normaly the instance of this object)
40396      */
40397     applySubTemplate : function(id, values, parent)
40398     {
40399         
40400         
40401         var t = this.tpls[id];
40402         
40403         
40404         try { 
40405             if(t.test && !t.test.call(this, values, parent)){
40406                 return '';
40407             }
40408         } catch(e) {
40409             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
40410             Roo.log(e.toString());
40411             Roo.log(t.test);
40412             return ''
40413         }
40414         try { 
40415             
40416             if(t.exec && t.exec.call(this, values, parent)){
40417                 return '';
40418             }
40419         } catch(e) {
40420             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
40421             Roo.log(e.toString());
40422             Roo.log(t.exec);
40423             return ''
40424         }
40425         try {
40426             var vs = t.target ? t.target.call(this, values, parent) : values;
40427             parent = t.target ? values : parent;
40428             if(t.target && vs instanceof Array){
40429                 var buf = [];
40430                 for(var i = 0, len = vs.length; i < len; i++){
40431                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
40432                 }
40433                 return buf.join('');
40434             }
40435             return t.compiled.call(this, vs, parent);
40436         } catch (e) {
40437             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
40438             Roo.log(e.toString());
40439             Roo.log(t.compiled);
40440             return '';
40441         }
40442     },
40443
40444     compileTpl : function(tpl)
40445     {
40446         var fm = Roo.util.Format;
40447         var useF = this.disableFormats !== true;
40448         var sep = Roo.isGecko ? "+" : ",";
40449         var undef = function(str) {
40450             Roo.log("Property not found :"  + str);
40451             return '';
40452         };
40453         
40454         var fn = function(m, name, format, args)
40455         {
40456             //Roo.log(arguments);
40457             args = args ? args.replace(/\\'/g,"'") : args;
40458             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
40459             if (typeof(format) == 'undefined') {
40460                 format= 'htmlEncode';
40461             }
40462             if (format == 'raw' ) {
40463                 format = false;
40464             }
40465             
40466             if(name.substr(0, 4) == 'xtpl'){
40467                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
40468             }
40469             
40470             // build an array of options to determine if value is undefined..
40471             
40472             // basically get 'xxxx.yyyy' then do
40473             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
40474             //    (function () { Roo.log("Property not found"); return ''; })() :
40475             //    ......
40476             
40477             var udef_ar = [];
40478             var lookfor = '';
40479             Roo.each(name.split('.'), function(st) {
40480                 lookfor += (lookfor.length ? '.': '') + st;
40481                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
40482             });
40483             
40484             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
40485             
40486             
40487             if(format && useF){
40488                 
40489                 args = args ? ',' + args : "";
40490                  
40491                 if(format.substr(0, 5) != "this."){
40492                     format = "fm." + format + '(';
40493                 }else{
40494                     format = 'this.call("'+ format.substr(5) + '", ';
40495                     args = ", values";
40496                 }
40497                 
40498                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
40499             }
40500              
40501             if (args.length) {
40502                 // called with xxyx.yuu:(test,test)
40503                 // change to ()
40504                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
40505             }
40506             // raw.. - :raw modifier..
40507             return "'"+ sep + udef_st  + name + ")"+sep+"'";
40508             
40509         };
40510         var body;
40511         // branched to use + in gecko and [].join() in others
40512         if(Roo.isGecko){
40513             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
40514                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
40515                     "';};};";
40516         }else{
40517             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
40518             body.push(tpl.body.replace(/(\r\n|\n)/g,
40519                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
40520             body.push("'].join('');};};");
40521             body = body.join('');
40522         }
40523         
40524         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
40525        
40526         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
40527         eval(body);
40528         
40529         return this;
40530     },
40531
40532     applyTemplate : function(values){
40533         return this.master.compiled.call(this, values, {});
40534         //var s = this.subs;
40535     },
40536
40537     apply : function(){
40538         return this.applyTemplate.apply(this, arguments);
40539     }
40540
40541  });
40542
40543 Roo.XTemplate.from = function(el){
40544     el = Roo.getDom(el);
40545     return new Roo.XTemplate(el.value || el.innerHTML);
40546 };/*
40547  * Original code for Roojs - LGPL
40548  * <script type="text/javascript">
40549  */
40550  
40551 /**
40552  * @class Roo.XComponent
40553  * A delayed Element creator...
40554  * Or a way to group chunks of interface together.
40555  * 
40556  * Mypart.xyx = new Roo.XComponent({
40557
40558     parent : 'Mypart.xyz', // empty == document.element.!!
40559     order : '001',
40560     name : 'xxxx'
40561     region : 'xxxx'
40562     disabled : function() {} 
40563      
40564     tree : function() { // return an tree of xtype declared components
40565         var MODULE = this;
40566         return 
40567         {
40568             xtype : 'NestedLayoutPanel',
40569             // technicall
40570         }
40571      ]
40572  *})
40573  *
40574  *
40575  * It can be used to build a big heiracy, with parent etc.
40576  * or you can just use this to render a single compoent to a dom element
40577  * MYPART.render(Roo.Element | String(id) | dom_element )
40578  * 
40579  * @extends Roo.util.Observable
40580  * @constructor
40581  * @param cfg {Object} configuration of component
40582  * 
40583  */
40584 Roo.XComponent = function(cfg) {
40585     Roo.apply(this, cfg);
40586     this.addEvents({ 
40587         /**
40588              * @event built
40589              * Fires when this the componnt is built
40590              * @param {Roo.XComponent} c the component
40591              */
40592         'built' : true
40593         
40594     });
40595     this.region = this.region || 'center'; // default..
40596     Roo.XComponent.register(this);
40597     this.modules = false;
40598     this.el = false; // where the layout goes..
40599     
40600     
40601 }
40602 Roo.extend(Roo.XComponent, Roo.util.Observable, {
40603     /**
40604      * @property el
40605      * The created element (with Roo.factory())
40606      * @type {Roo.Layout}
40607      */
40608     el  : false,
40609     
40610     /**
40611      * @property el
40612      * for BC  - use el in new code
40613      * @type {Roo.Layout}
40614      */
40615     panel : false,
40616     
40617     /**
40618      * @property layout
40619      * for BC  - use el in new code
40620      * @type {Roo.Layout}
40621      */
40622     layout : false,
40623     
40624      /**
40625      * @cfg {Function|boolean} disabled
40626      * If this module is disabled by some rule, return true from the funtion
40627      */
40628     disabled : false,
40629     
40630     /**
40631      * @cfg {String} parent 
40632      * Name of parent element which it get xtype added to..
40633      */
40634     parent: false,
40635     
40636     /**
40637      * @cfg {String} order
40638      * Used to set the order in which elements are created (usefull for multiple tabs)
40639      */
40640     
40641     order : false,
40642     /**
40643      * @cfg {String} name
40644      * String to display while loading.
40645      */
40646     name : false,
40647     /**
40648      * @cfg {String} region
40649      * Region to render component to (defaults to center)
40650      */
40651     region : 'center',
40652     
40653     /**
40654      * @cfg {Array} items
40655      * A single item array - the first element is the root of the tree..
40656      * It's done this way to stay compatible with the Xtype system...
40657      */
40658     items : false,
40659     
40660     /**
40661      * @property _tree
40662      * The method that retuns the tree of parts that make up this compoennt 
40663      * @type {function}
40664      */
40665     _tree  : false,
40666     
40667      /**
40668      * render
40669      * render element to dom or tree
40670      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
40671      */
40672     
40673     render : function(el)
40674     {
40675         
40676         el = el || false;
40677         var hp = this.parent ? 1 : 0;
40678         
40679         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
40680             // if parent is a '#.....' string, then let's use that..
40681             var ename = this.parent.substr(1)
40682             this.parent = false;
40683             el = Roo.get(ename);
40684             if (!el) {
40685                 Roo.log("Warning - element can not be found :#" + ename );
40686                 return;
40687             }
40688         }
40689         
40690         
40691         if (!this.parent) {
40692             
40693             el = el ? Roo.get(el) : false;      
40694             
40695             // it's a top level one..
40696             this.parent =  {
40697                 el : new Roo.BorderLayout(el || document.body, {
40698                 
40699                      center: {
40700                          titlebar: false,
40701                          autoScroll:false,
40702                          closeOnTab: true,
40703                          tabPosition: 'top',
40704                           //resizeTabs: true,
40705                          alwaysShowTabs: el && hp? false :  true,
40706                          hideTabs: el || !hp ? true :  false,
40707                          minTabWidth: 140
40708                      }
40709                  })
40710             }
40711         }
40712         
40713                 if (!this.parent.el) {
40714                         // probably an old style ctor, which has been disabled.
40715                         return;
40716                         
40717                 }
40718                 // The 'tree' method is  '_tree now' 
40719             
40720         var tree = this._tree ? this._tree() : this.tree();
40721         tree.region = tree.region || this.region;
40722         this.el = this.parent.el.addxtype(tree);
40723         this.fireEvent('built', this);
40724         
40725         this.panel = this.el;
40726         this.layout = this.panel.layout;
40727                 this.parentLayout = this.parent.layout  || false;  
40728          
40729     }
40730     
40731 });
40732
40733 Roo.apply(Roo.XComponent, {
40734     /**
40735      * @property  hideProgress
40736      * true to disable the building progress bar.. usefull on single page renders.
40737      * @type Boolean
40738      */
40739     hideProgress : false,
40740     /**
40741      * @property  buildCompleted
40742      * True when the builder has completed building the interface.
40743      * @type Boolean
40744      */
40745     buildCompleted : false,
40746      
40747     /**
40748      * @property  topModule
40749      * the upper most module - uses document.element as it's constructor.
40750      * @type Object
40751      */
40752      
40753     topModule  : false,
40754       
40755     /**
40756      * @property  modules
40757      * array of modules to be created by registration system.
40758      * @type {Array} of Roo.XComponent
40759      */
40760     
40761     modules : [],
40762     /**
40763      * @property  elmodules
40764      * array of modules to be created by which use #ID 
40765      * @type {Array} of Roo.XComponent
40766      */
40767      
40768     elmodules : [],
40769
40770     
40771     /**
40772      * Register components to be built later.
40773      *
40774      * This solves the following issues
40775      * - Building is not done on page load, but after an authentication process has occured.
40776      * - Interface elements are registered on page load
40777      * - Parent Interface elements may not be loaded before child, so this handles that..
40778      * 
40779      *
40780      * example:
40781      * 
40782      * MyApp.register({
40783           order : '000001',
40784           module : 'Pman.Tab.projectMgr',
40785           region : 'center',
40786           parent : 'Pman.layout',
40787           disabled : false,  // or use a function..
40788         })
40789      
40790      * * @param {Object} details about module
40791      */
40792     register : function(obj) {
40793                 
40794         Roo.XComponent.event.fireEvent('register', obj);
40795         switch(typeof(obj.disabled) ) {
40796                 
40797             case 'undefined':
40798                 break;
40799             
40800             case 'function':
40801                 if ( obj.disabled() ) {
40802                         return;
40803                 }
40804                 break;
40805             
40806             default:
40807                 if (obj.disabled) {
40808                         return;
40809                 }
40810                 break;
40811         }
40812                 
40813         this.modules.push(obj);
40814          
40815     },
40816     /**
40817      * convert a string to an object..
40818      * eg. 'AAA.BBB' -> finds AAA.BBB
40819
40820      */
40821     
40822     toObject : function(str)
40823     {
40824         if (!str || typeof(str) == 'object') {
40825             return str;
40826         }
40827         if (str.substring(0,1) == '#') {
40828             return str;
40829         }
40830
40831         var ar = str.split('.');
40832         var rt, o;
40833         rt = ar.shift();
40834             /** eval:var:o */
40835         try {
40836             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
40837         } catch (e) {
40838             throw "Module not found : " + str;
40839         }
40840         
40841         if (o === false) {
40842             throw "Module not found : " + str;
40843         }
40844         Roo.each(ar, function(e) {
40845             if (typeof(o[e]) == 'undefined') {
40846                 throw "Module not found : " + str;
40847             }
40848             o = o[e];
40849         });
40850         
40851         return o;
40852         
40853     },
40854     
40855     
40856     /**
40857      * move modules into their correct place in the tree..
40858      * 
40859      */
40860     preBuild : function ()
40861     {
40862         var _t = this;
40863         Roo.each(this.modules , function (obj)
40864         {
40865             Roo.XComponent.event.fireEvent('beforebuild', obj);
40866             
40867             var opar = obj.parent;
40868             try { 
40869                 obj.parent = this.toObject(opar);
40870             } catch(e) {
40871                 Roo.log("parent:toObject failed: " + e.toString());
40872                 return;
40873             }
40874             
40875             if (!obj.parent) {
40876                 Roo.debug && Roo.log("GOT top level module");
40877                 Roo.debug && Roo.log(obj);
40878                 obj.modules = new Roo.util.MixedCollection(false, 
40879                     function(o) { return o.order + '' }
40880                 );
40881                 this.topModule = obj;
40882                 return;
40883             }
40884                         // parent is a string (usually a dom element name..)
40885             if (typeof(obj.parent) == 'string') {
40886                 this.elmodules.push(obj);
40887                 return;
40888             }
40889             if (obj.parent.constructor != Roo.XComponent) {
40890                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
40891             }
40892             if (!obj.parent.modules) {
40893                 obj.parent.modules = new Roo.util.MixedCollection(false, 
40894                     function(o) { return o.order + '' }
40895                 );
40896             }
40897             if (obj.parent.disabled) {
40898                 obj.disabled = true;
40899             }
40900             obj.parent.modules.add(obj);
40901         }, this);
40902     },
40903     
40904      /**
40905      * make a list of modules to build.
40906      * @return {Array} list of modules. 
40907      */ 
40908     
40909     buildOrder : function()
40910     {
40911         var _this = this;
40912         var cmp = function(a,b) {   
40913             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
40914         };
40915         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
40916             throw "No top level modules to build";
40917         }
40918         
40919         // make a flat list in order of modules to build.
40920         var mods = this.topModule ? [ this.topModule ] : [];
40921                 
40922         
40923         // elmodules (is a list of DOM based modules )
40924         Roo.each(this.elmodules, function(e) {
40925             mods.push(e);
40926             if (!this.topModule &&
40927                 typeof(e.parent) == 'string' &&
40928                 e.parent.substring(0,1) == '#' &&
40929                 Roo.get(e.parent.substr(1))
40930                ) {
40931                 
40932                 _this.topModule = e;
40933             }
40934             
40935         });
40936
40937         
40938         // add modules to their parents..
40939         var addMod = function(m) {
40940             Roo.debug && Roo.log("build Order: add: " + m.name);
40941                 
40942             mods.push(m);
40943             if (m.modules && !m.disabled) {
40944                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
40945                 m.modules.keySort('ASC',  cmp );
40946                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
40947     
40948                 m.modules.each(addMod);
40949             } else {
40950                 Roo.debug && Roo.log("build Order: no child modules");
40951             }
40952             // not sure if this is used any more..
40953             if (m.finalize) {
40954                 m.finalize.name = m.name + " (clean up) ";
40955                 mods.push(m.finalize);
40956             }
40957             
40958         }
40959         if (this.topModule && this.topModule.modules) { 
40960             this.topModule.modules.keySort('ASC',  cmp );
40961             this.topModule.modules.each(addMod);
40962         } 
40963         return mods;
40964     },
40965     
40966      /**
40967      * Build the registered modules.
40968      * @param {Object} parent element.
40969      * @param {Function} optional method to call after module has been added.
40970      * 
40971      */ 
40972    
40973     build : function() 
40974     {
40975         
40976         this.preBuild();
40977         var mods = this.buildOrder();
40978       
40979         //this.allmods = mods;
40980         //Roo.debug && Roo.log(mods);
40981         //return;
40982         if (!mods.length) { // should not happen
40983             throw "NO modules!!!";
40984         }
40985         
40986         
40987         var msg = "Building Interface...";
40988         // flash it up as modal - so we store the mask!?
40989         if (!this.hideProgress) {
40990             Roo.MessageBox.show({ title: 'loading' });
40991             Roo.MessageBox.show({
40992                title: "Please wait...",
40993                msg: msg,
40994                width:450,
40995                progress:true,
40996                closable:false,
40997                modal: false
40998               
40999             });
41000         }
41001         var total = mods.length;
41002         
41003         var _this = this;
41004         var progressRun = function() {
41005             if (!mods.length) {
41006                 Roo.debug && Roo.log('hide?');
41007                 if (!this.hideProgress) {
41008                     Roo.MessageBox.hide();
41009                 }
41010                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
41011                 
41012                 // THE END...
41013                 return false;   
41014             }
41015             
41016             var m = mods.shift();
41017             
41018             
41019             Roo.debug && Roo.log(m);
41020             // not sure if this is supported any more.. - modules that are are just function
41021             if (typeof(m) == 'function') { 
41022                 m.call(this);
41023                 return progressRun.defer(10, _this);
41024             } 
41025             
41026             
41027             msg = "Building Interface " + (total  - mods.length) + 
41028                     " of " + total + 
41029                     (m.name ? (' - ' + m.name) : '');
41030                         Roo.debug && Roo.log(msg);
41031             if (!this.hideProgress) { 
41032                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
41033             }
41034             
41035          
41036             // is the module disabled?
41037             var disabled = (typeof(m.disabled) == 'function') ?
41038                 m.disabled.call(m.module.disabled) : m.disabled;    
41039             
41040             
41041             if (disabled) {
41042                 return progressRun(); // we do not update the display!
41043             }
41044             
41045             // now build 
41046             
41047                         
41048                         
41049             m.render();
41050             // it's 10 on top level, and 1 on others??? why...
41051             return progressRun.defer(10, _this);
41052              
41053         }
41054         progressRun.defer(1, _this);
41055      
41056         
41057         
41058     },
41059         
41060         
41061         /**
41062          * Event Object.
41063          *
41064          *
41065          */
41066         event: false, 
41067     /**
41068          * wrapper for event.on - aliased later..  
41069          * Typically use to register a event handler for register:
41070          *
41071          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
41072          *
41073          */
41074     on : false
41075    
41076     
41077     
41078 });
41079
41080 Roo.XComponent.event = new Roo.util.Observable({
41081                 events : { 
41082                         /**
41083                          * @event register
41084                          * Fires when an Component is registered,
41085                          * set the disable property on the Component to stop registration.
41086                          * @param {Roo.XComponent} c the component being registerd.
41087                          * 
41088                          */
41089                         'register' : true,
41090             /**
41091                          * @event beforebuild
41092                          * Fires before each Component is built
41093                          * can be used to apply permissions.
41094                          * @param {Roo.XComponent} c the component being registerd.
41095                          * 
41096                          */
41097                         'beforebuild' : true,
41098                         /**
41099                          * @event buildcomplete
41100                          * Fires on the top level element when all elements have been built
41101                          * @param {Roo.XComponent} the top level component.
41102                          */
41103                         'buildcomplete' : true
41104                         
41105                 }
41106 });
41107
41108 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
41109