roojs-core.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     var onStop = function(e){
3044         dragEl = null;
3045         clearProc();
3046     };
3047     
3048     var triggerRefresh = function(){
3049         if(ddm.dragCurrent){
3050              ddm.refreshCache(ddm.dragCurrent.groups);
3051         }
3052     };
3053     
3054     var doScroll = function(){
3055         if(ddm.dragCurrent){
3056             var dds = Roo.dd.ScrollManager;
3057             if(!dds.animate){
3058                 if(proc.el.scroll(proc.dir, dds.increment)){
3059                     triggerRefresh();
3060                 }
3061             }else{
3062                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3063             }
3064         }
3065     };
3066     
3067     var clearProc = function(){
3068         if(proc.id){
3069             clearInterval(proc.id);
3070         }
3071         proc.id = 0;
3072         proc.el = null;
3073         proc.dir = "";
3074     };
3075     
3076     var startProc = function(el, dir){
3077         clearProc();
3078         proc.el = el;
3079         proc.dir = dir;
3080         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3081     };
3082     
3083     var onFire = function(e, isDrop){
3084         if(isDrop || !ddm.dragCurrent){ return; }
3085         var dds = Roo.dd.ScrollManager;
3086         if(!dragEl || dragEl != ddm.dragCurrent){
3087             dragEl = ddm.dragCurrent;
3088             // refresh regions on drag start
3089             dds.refreshCache();
3090         }
3091         
3092         var xy = Roo.lib.Event.getXY(e);
3093         var pt = new Roo.lib.Point(xy[0], xy[1]);
3094         for(var id in els){
3095             var el = els[id], r = el._region;
3096             if(r && r.contains(pt) && el.isScrollable()){
3097                 if(r.bottom - pt.y <= dds.thresh){
3098                     if(proc.el != el){
3099                         startProc(el, "down");
3100                     }
3101                     return;
3102                 }else if(r.right - pt.x <= dds.thresh){
3103                     if(proc.el != el){
3104                         startProc(el, "left");
3105                     }
3106                     return;
3107                 }else if(pt.y - r.top <= dds.thresh){
3108                     if(proc.el != el){
3109                         startProc(el, "up");
3110                     }
3111                     return;
3112                 }else if(pt.x - r.left <= dds.thresh){
3113                     if(proc.el != el){
3114                         startProc(el, "right");
3115                     }
3116                     return;
3117                 }
3118             }
3119         }
3120         clearProc();
3121     };
3122     
3123     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3124     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3125     
3126     return {
3127         /**
3128          * Registers new overflow element(s) to auto scroll
3129          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3130          */
3131         register : function(el){
3132             if(el instanceof Array){
3133                 for(var i = 0, len = el.length; i < len; i++) {
3134                         this.register(el[i]);
3135                 }
3136             }else{
3137                 el = Roo.get(el);
3138                 els[el.id] = el;
3139             }
3140         },
3141         
3142         /**
3143          * Unregisters overflow element(s) so they are no longer scrolled
3144          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3145          */
3146         unregister : function(el){
3147             if(el instanceof Array){
3148                 for(var i = 0, len = el.length; i < len; i++) {
3149                         this.unregister(el[i]);
3150                 }
3151             }else{
3152                 el = Roo.get(el);
3153                 delete els[el.id];
3154             }
3155         },
3156         
3157         /**
3158          * The number of pixels from the edge of a container the pointer needs to be to 
3159          * trigger scrolling (defaults to 25)
3160          * @type Number
3161          */
3162         thresh : 25,
3163         
3164         /**
3165          * The number of pixels to scroll in each scroll increment (defaults to 50)
3166          * @type Number
3167          */
3168         increment : 100,
3169         
3170         /**
3171          * The frequency of scrolls in milliseconds (defaults to 500)
3172          * @type Number
3173          */
3174         frequency : 500,
3175         
3176         /**
3177          * True to animate the scroll (defaults to true)
3178          * @type Boolean
3179          */
3180         animate: true,
3181         
3182         /**
3183          * The animation duration in seconds - 
3184          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3185          * @type Number
3186          */
3187         animDuration: .4,
3188         
3189         /**
3190          * Manually trigger a cache refresh.
3191          */
3192         refreshCache : function(){
3193             for(var id in els){
3194                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3195                     els[id]._region = els[id].getRegion();
3196                 }
3197             }
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210  
3211
3212 /**
3213  * @class Roo.dd.Registry
3214  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3215  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3216  * @singleton
3217  */
3218 Roo.dd.Registry = function(){
3219     var elements = {}; 
3220     var handles = {}; 
3221     var autoIdSeed = 0;
3222
3223     var getId = function(el, autogen){
3224         if(typeof el == "string"){
3225             return el;
3226         }
3227         var id = el.id;
3228         if(!id && autogen !== false){
3229             id = "roodd-" + (++autoIdSeed);
3230             el.id = id;
3231         }
3232         return id;
3233     };
3234     
3235     return {
3236     /**
3237      * Register a drag drop element
3238      * @param {String|HTMLElement} element The id or DOM node to register
3239      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3240      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3241      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3242      * populated in the data object (if applicable):
3243      * <pre>
3244 Value      Description<br />
3245 ---------  ------------------------------------------<br />
3246 handles    Array of DOM nodes that trigger dragging<br />
3247            for the element being registered<br />
3248 isHandle   True if the element passed in triggers<br />
3249            dragging itself, else false
3250 </pre>
3251      */
3252         register : function(el, data){
3253             data = data || {};
3254             if(typeof el == "string"){
3255                 el = document.getElementById(el);
3256             }
3257             data.ddel = el;
3258             elements[getId(el)] = data;
3259             if(data.isHandle !== false){
3260                 handles[data.ddel.id] = data;
3261             }
3262             if(data.handles){
3263                 var hs = data.handles;
3264                 for(var i = 0, len = hs.length; i < len; i++){
3265                         handles[getId(hs[i])] = data;
3266                 }
3267             }
3268         },
3269
3270     /**
3271      * Unregister a drag drop element
3272      * @param {String|HTMLElement}  element The id or DOM node to unregister
3273      */
3274         unregister : function(el){
3275             var id = getId(el, false);
3276             var data = elements[id];
3277             if(data){
3278                 delete elements[id];
3279                 if(data.handles){
3280                     var hs = data.handles;
3281                     for(var i = 0, len = hs.length; i < len; i++){
3282                         delete handles[getId(hs[i], false)];
3283                     }
3284                 }
3285             }
3286         },
3287
3288     /**
3289      * Returns the handle registered for a DOM Node by id
3290      * @param {String|HTMLElement} id The DOM node or id to look up
3291      * @return {Object} handle The custom handle data
3292      */
3293         getHandle : function(id){
3294             if(typeof id != "string"){ // must be element?
3295                 id = id.id;
3296             }
3297             return handles[id];
3298         },
3299
3300     /**
3301      * Returns the handle that is registered for the DOM node that is the target of the event
3302      * @param {Event} e The event
3303      * @return {Object} handle The custom handle data
3304      */
3305         getHandleFromEvent : function(e){
3306             var t = Roo.lib.Event.getTarget(e);
3307             return t ? handles[t.id] : null;
3308         },
3309
3310     /**
3311      * Returns a custom data object that is registered for a DOM node by id
3312      * @param {String|HTMLElement} id The DOM node or id to look up
3313      * @return {Object} data The custom data
3314      */
3315         getTarget : function(id){
3316             if(typeof id != "string"){ // must be element?
3317                 id = id.id;
3318             }
3319             return elements[id];
3320         },
3321
3322     /**
3323      * Returns a custom data object that is registered for the DOM node that is the target of the event
3324      * @param {Event} e The event
3325      * @return {Object} data The custom data
3326      */
3327         getTargetFromEvent : function(e){
3328             var t = Roo.lib.Event.getTarget(e);
3329             return t ? elements[t.id] || handles[t.id] : null;
3330         }
3331     };
3332 }();/*
3333  * Based on:
3334  * Ext JS Library 1.1.1
3335  * Copyright(c) 2006-2007, Ext JS, LLC.
3336  *
3337  * Originally Released Under LGPL - original licence link has changed is not relivant.
3338  *
3339  * Fork - LGPL
3340  * <script type="text/javascript">
3341  */
3342  
3343
3344 /**
3345  * @class Roo.dd.StatusProxy
3346  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3347  * default drag proxy used by all Roo.dd components.
3348  * @constructor
3349  * @param {Object} config
3350  */
3351 Roo.dd.StatusProxy = function(config){
3352     Roo.apply(this, config);
3353     this.id = this.id || Roo.id();
3354     this.el = new Roo.Layer({
3355         dh: {
3356             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3357                 {tag: "div", cls: "x-dd-drop-icon"},
3358                 {tag: "div", cls: "x-dd-drag-ghost"}
3359             ]
3360         }, 
3361         shadow: !config || config.shadow !== false
3362     });
3363     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3364     this.dropStatus = this.dropNotAllowed;
3365 };
3366
3367 Roo.dd.StatusProxy.prototype = {
3368     /**
3369      * @cfg {String} dropAllowed
3370      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3371      */
3372     dropAllowed : "x-dd-drop-ok",
3373     /**
3374      * @cfg {String} dropNotAllowed
3375      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3376      */
3377     dropNotAllowed : "x-dd-drop-nodrop",
3378
3379     /**
3380      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3381      * over the current target element.
3382      * @param {String} cssClass The css class for the new drop status indicator image
3383      */
3384     setStatus : function(cssClass){
3385         cssClass = cssClass || this.dropNotAllowed;
3386         if(this.dropStatus != cssClass){
3387             this.el.replaceClass(this.dropStatus, cssClass);
3388             this.dropStatus = cssClass;
3389         }
3390     },
3391
3392     /**
3393      * Resets the status indicator to the default dropNotAllowed value
3394      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3395      */
3396     reset : function(clearGhost){
3397         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3398         this.dropStatus = this.dropNotAllowed;
3399         if(clearGhost){
3400             this.ghost.update("");
3401         }
3402     },
3403
3404     /**
3405      * Updates the contents of the ghost element
3406      * @param {String} html The html that will replace the current innerHTML of the ghost element
3407      */
3408     update : function(html){
3409         if(typeof html == "string"){
3410             this.ghost.update(html);
3411         }else{
3412             this.ghost.update("");
3413             html.style.margin = "0";
3414             this.ghost.dom.appendChild(html);
3415         }
3416         // ensure float = none set?? cant remember why though.
3417         var el = this.ghost.dom.firstChild;
3418                 if(el){
3419                         Roo.fly(el).setStyle('float', 'none');
3420                 }
3421     },
3422     
3423     /**
3424      * Returns the underlying proxy {@link Roo.Layer}
3425      * @return {Roo.Layer} el
3426     */
3427     getEl : function(){
3428         return this.el;
3429     },
3430
3431     /**
3432      * Returns the ghost element
3433      * @return {Roo.Element} el
3434      */
3435     getGhost : function(){
3436         return this.ghost;
3437     },
3438
3439     /**
3440      * Hides the proxy
3441      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3442      */
3443     hide : function(clear){
3444         this.el.hide();
3445         if(clear){
3446             this.reset(true);
3447         }
3448     },
3449
3450     /**
3451      * Stops the repair animation if it's currently running
3452      */
3453     stop : function(){
3454         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3455             this.anim.stop();
3456         }
3457     },
3458
3459     /**
3460      * Displays this proxy
3461      */
3462     show : function(){
3463         this.el.show();
3464     },
3465
3466     /**
3467      * Force the Layer to sync its shadow and shim positions to the element
3468      */
3469     sync : function(){
3470         this.el.sync();
3471     },
3472
3473     /**
3474      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3475      * invalid drop operation by the item being dragged.
3476      * @param {Array} xy The XY position of the element ([x, y])
3477      * @param {Function} callback The function to call after the repair is complete
3478      * @param {Object} scope The scope in which to execute the callback
3479      */
3480     repair : function(xy, callback, scope){
3481         this.callback = callback;
3482         this.scope = scope;
3483         if(xy && this.animRepair !== false){
3484             this.el.addClass("x-dd-drag-repair");
3485             this.el.hideUnders(true);
3486             this.anim = this.el.shift({
3487                 duration: this.repairDuration || .5,
3488                 easing: 'easeOut',
3489                 xy: xy,
3490                 stopFx: true,
3491                 callback: this.afterRepair,
3492                 scope: this
3493             });
3494         }else{
3495             this.afterRepair();
3496         }
3497     },
3498
3499     // private
3500     afterRepair : function(){
3501         this.hide(true);
3502         if(typeof this.callback == "function"){
3503             this.callback.call(this.scope || this);
3504         }
3505         this.callback = null;
3506         this.scope = null;
3507     }
3508 };/*
3509  * Based on:
3510  * Ext JS Library 1.1.1
3511  * Copyright(c) 2006-2007, Ext JS, LLC.
3512  *
3513  * Originally Released Under LGPL - original licence link has changed is not relivant.
3514  *
3515  * Fork - LGPL
3516  * <script type="text/javascript">
3517  */
3518
3519 /**
3520  * @class Roo.dd.DragSource
3521  * @extends Roo.dd.DDProxy
3522  * A simple class that provides the basic implementation needed to make any element draggable.
3523  * @constructor
3524  * @param {String/HTMLElement/Element} el The container element
3525  * @param {Object} config
3526  */
3527 Roo.dd.DragSource = function(el, config){
3528     this.el = Roo.get(el);
3529     this.dragData = {};
3530     
3531     Roo.apply(this, config);
3532     
3533     if(!this.proxy){
3534         this.proxy = new Roo.dd.StatusProxy();
3535     }
3536
3537     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3538           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3539     
3540     this.dragging = false;
3541 };
3542
3543 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3544     /**
3545      * @cfg {String} dropAllowed
3546      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3547      */
3548     dropAllowed : "x-dd-drop-ok",
3549     /**
3550      * @cfg {String} dropNotAllowed
3551      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3552      */
3553     dropNotAllowed : "x-dd-drop-nodrop",
3554
3555     /**
3556      * Returns the data object associated with this drag source
3557      * @return {Object} data An object containing arbitrary data
3558      */
3559     getDragData : function(e){
3560         return this.dragData;
3561     },
3562
3563     // private
3564     onDragEnter : function(e, id){
3565         var target = Roo.dd.DragDropMgr.getDDById(id);
3566         this.cachedTarget = target;
3567         if(this.beforeDragEnter(target, e, id) !== false){
3568             if(target.isNotifyTarget){
3569                 var status = target.notifyEnter(this, e, this.dragData);
3570                 this.proxy.setStatus(status);
3571             }else{
3572                 this.proxy.setStatus(this.dropAllowed);
3573             }
3574             
3575             if(this.afterDragEnter){
3576                 /**
3577                  * An empty function by default, but provided so that you can perform a custom action
3578                  * when the dragged item enters the drop target by providing an implementation.
3579                  * @param {Roo.dd.DragDrop} target The drop target
3580                  * @param {Event} e The event object
3581                  * @param {String} id The id of the dragged element
3582                  * @method afterDragEnter
3583                  */
3584                 this.afterDragEnter(target, e, id);
3585             }
3586         }
3587     },
3588
3589     /**
3590      * An empty function by default, but provided so that you can perform a custom action
3591      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3592      * @param {Roo.dd.DragDrop} target The drop target
3593      * @param {Event} e The event object
3594      * @param {String} id The id of the dragged element
3595      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3596      */
3597     beforeDragEnter : function(target, e, id){
3598         return true;
3599     },
3600
3601     // private
3602     alignElWithMouse: function() {
3603         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3604         this.proxy.sync();
3605     },
3606
3607     // private
3608     onDragOver : function(e, id){
3609         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3610         if(this.beforeDragOver(target, e, id) !== false){
3611             if(target.isNotifyTarget){
3612                 var status = target.notifyOver(this, e, this.dragData);
3613                 this.proxy.setStatus(status);
3614             }
3615
3616             if(this.afterDragOver){
3617                 /**
3618                  * An empty function by default, but provided so that you can perform a custom action
3619                  * while the dragged item is over the drop target by providing an implementation.
3620                  * @param {Roo.dd.DragDrop} target The drop target
3621                  * @param {Event} e The event object
3622                  * @param {String} id The id of the dragged element
3623                  * @method afterDragOver
3624                  */
3625                 this.afterDragOver(target, e, id);
3626             }
3627         }
3628     },
3629
3630     /**
3631      * An empty function by default, but provided so that you can perform a custom action
3632      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3633      * @param {Roo.dd.DragDrop} target The drop target
3634      * @param {Event} e The event object
3635      * @param {String} id The id of the dragged element
3636      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3637      */
3638     beforeDragOver : function(target, e, id){
3639         return true;
3640     },
3641
3642     // private
3643     onDragOut : function(e, id){
3644         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3645         if(this.beforeDragOut(target, e, id) !== false){
3646             if(target.isNotifyTarget){
3647                 target.notifyOut(this, e, this.dragData);
3648             }
3649             this.proxy.reset();
3650             if(this.afterDragOut){
3651                 /**
3652                  * An empty function by default, but provided so that you can perform a custom action
3653                  * after the dragged item is dragged out of the target without dropping.
3654                  * @param {Roo.dd.DragDrop} target The drop target
3655                  * @param {Event} e The event object
3656                  * @param {String} id The id of the dragged element
3657                  * @method afterDragOut
3658                  */
3659                 this.afterDragOut(target, e, id);
3660             }
3661         }
3662         this.cachedTarget = null;
3663     },
3664
3665     /**
3666      * An empty function by default, but provided so that you can perform a custom action before the dragged
3667      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
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      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3672      */
3673     beforeDragOut : function(target, e, id){
3674         return true;
3675     },
3676     
3677     // private
3678     onDragDrop : function(e, id){
3679         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3680         if(this.beforeDragDrop(target, e, id) !== false){
3681             if(target.isNotifyTarget){
3682                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3683                     this.onValidDrop(target, e, id);
3684                 }else{
3685                     this.onInvalidDrop(target, e, id);
3686                 }
3687             }else{
3688                 this.onValidDrop(target, e, id);
3689             }
3690             
3691             if(this.afterDragDrop){
3692                 /**
3693                  * An empty function by default, but provided so that you can perform a custom action
3694                  * after a valid drag drop has occurred by providing an implementation.
3695                  * @param {Roo.dd.DragDrop} target The drop target
3696                  * @param {Event} e The event object
3697                  * @param {String} id The id of the dropped element
3698                  * @method afterDragDrop
3699                  */
3700                 this.afterDragDrop(target, e, id);
3701             }
3702         }
3703         delete this.cachedTarget;
3704     },
3705
3706     /**
3707      * An empty function by default, but provided so that you can perform a custom action before the dragged
3708      * item is dropped onto the target and optionally cancel the onDragDrop.
3709      * @param {Roo.dd.DragDrop} target The drop target
3710      * @param {Event} e The event object
3711      * @param {String} id The id of the dragged element
3712      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3713      */
3714     beforeDragDrop : function(target, e, id){
3715         return true;
3716     },
3717
3718     // private
3719     onValidDrop : function(target, e, id){
3720         this.hideProxy();
3721         if(this.afterValidDrop){
3722             /**
3723              * An empty function by default, but provided so that you can perform a custom action
3724              * after a valid drop has occurred by providing an implementation.
3725              * @param {Object} target The target DD 
3726              * @param {Event} e The event object
3727              * @param {String} id The id of the dropped element
3728              * @method afterInvalidDrop
3729              */
3730             this.afterValidDrop(target, e, id);
3731         }
3732     },
3733
3734     // private
3735     getRepairXY : function(e, data){
3736         return this.el.getXY();  
3737     },
3738
3739     // private
3740     onInvalidDrop : function(target, e, id){
3741         this.beforeInvalidDrop(target, e, id);
3742         if(this.cachedTarget){
3743             if(this.cachedTarget.isNotifyTarget){
3744                 this.cachedTarget.notifyOut(this, e, this.dragData);
3745             }
3746             this.cacheTarget = null;
3747         }
3748         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3749
3750         if(this.afterInvalidDrop){
3751             /**
3752              * An empty function by default, but provided so that you can perform a custom action
3753              * after an invalid drop has occurred by providing an implementation.
3754              * @param {Event} e The event object
3755              * @param {String} id The id of the dropped element
3756              * @method afterInvalidDrop
3757              */
3758             this.afterInvalidDrop(e, id);
3759         }
3760     },
3761
3762     // private
3763     afterRepair : function(){
3764         if(Roo.enableFx){
3765             this.el.highlight(this.hlColor || "c3daf9");
3766         }
3767         this.dragging = false;
3768     },
3769
3770     /**
3771      * An empty function by default, but provided so that you can perform a custom action after an invalid
3772      * drop has occurred.
3773      * @param {Roo.dd.DragDrop} target The drop target
3774      * @param {Event} e The event object
3775      * @param {String} id The id of the dragged element
3776      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3777      */
3778     beforeInvalidDrop : function(target, e, id){
3779         return true;
3780     },
3781
3782     // private
3783     handleMouseDown : function(e){
3784         if(this.dragging) {
3785             return;
3786         }
3787         var data = this.getDragData(e);
3788         if(data && this.onBeforeDrag(data, e) !== false){
3789             this.dragData = data;
3790             this.proxy.stop();
3791             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3792         } 
3793     },
3794
3795     /**
3796      * An empty function by default, but provided so that you can perform a custom action before the initial
3797      * drag event begins and optionally cancel it.
3798      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3799      * @param {Event} e The event object
3800      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3801      */
3802     onBeforeDrag : function(data, e){
3803         return true;
3804     },
3805
3806     /**
3807      * An empty function by default, but provided so that you can perform a custom action once the initial
3808      * drag event has begun.  The drag cannot be canceled from this function.
3809      * @param {Number} x The x position of the click on the dragged object
3810      * @param {Number} y The y position of the click on the dragged object
3811      */
3812     onStartDrag : Roo.emptyFn,
3813
3814     // private - YUI override
3815     startDrag : function(x, y){
3816         this.proxy.reset();
3817         this.dragging = true;
3818         this.proxy.update("");
3819         this.onInitDrag(x, y);
3820         this.proxy.show();
3821     },
3822
3823     // private
3824     onInitDrag : function(x, y){
3825         var clone = this.el.dom.cloneNode(true);
3826         clone.id = Roo.id(); // prevent duplicate ids
3827         this.proxy.update(clone);
3828         this.onStartDrag(x, y);
3829         return true;
3830     },
3831
3832     /**
3833      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3834      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3835      */
3836     getProxy : function(){
3837         return this.proxy;  
3838     },
3839
3840     /**
3841      * Hides the drag source's {@link Roo.dd.StatusProxy}
3842      */
3843     hideProxy : function(){
3844         this.proxy.hide();  
3845         this.proxy.reset(true);
3846         this.dragging = false;
3847     },
3848
3849     // private
3850     triggerCacheRefresh : function(){
3851         Roo.dd.DDM.refreshCache(this.groups);
3852     },
3853
3854     // private - override to prevent hiding
3855     b4EndDrag: function(e) {
3856     },
3857
3858     // private - override to prevent moving
3859     endDrag : function(e){
3860         this.onEndDrag(this.dragData, e);
3861     },
3862
3863     // private
3864     onEndDrag : function(data, e){
3865     },
3866     
3867     // private - pin to cursor
3868     autoOffset : function(x, y) {
3869         this.setDelta(-12, -20);
3870     }    
3871 });/*
3872  * Based on:
3873  * Ext JS Library 1.1.1
3874  * Copyright(c) 2006-2007, Ext JS, LLC.
3875  *
3876  * Originally Released Under LGPL - original licence link has changed is not relivant.
3877  *
3878  * Fork - LGPL
3879  * <script type="text/javascript">
3880  */
3881
3882
3883 /**
3884  * @class Roo.dd.DropTarget
3885  * @extends Roo.dd.DDTarget
3886  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3887  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3888  * @constructor
3889  * @param {String/HTMLElement/Element} el The container element
3890  * @param {Object} config
3891  */
3892 Roo.dd.DropTarget = function(el, config){
3893     this.el = Roo.get(el);
3894     
3895     var listeners = false; ;
3896     if (config && config.listeners) {
3897         listeners= config.listeners;
3898         delete config.listeners;
3899     }
3900     Roo.apply(this, config);
3901     
3902     if(this.containerScroll){
3903         Roo.dd.ScrollManager.register(this.el);
3904     }
3905     this.addEvents( {
3906          /**
3907          * @scope Roo.dd.DropTarget
3908          */
3909          
3910          /**
3911          * @event enter
3912          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3913          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3914          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3915          * 
3916          * IMPORTANT : it should set this.overClass and this.dropAllowed
3917          * 
3918          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3919          * @param {Event} e The event
3920          * @param {Object} data An object containing arbitrary data supplied by the drag source
3921          */
3922         "enter" : true,
3923         
3924          /**
3925          * @event over
3926          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3927          * This method will be called on every mouse movement while the drag source is over the drop target.
3928          * This default implementation simply returns the dropAllowed config value.
3929          * 
3930          * IMPORTANT : it should set 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          */
3937         "over" : true,
3938         /**
3939          * @event out
3940          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3941          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3942          * overClass (if any) from the drop element.
3943          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3944          * @param {Event} e The event
3945          * @param {Object} data An object containing arbitrary data supplied by the drag source
3946          */
3947          "out" : true,
3948          
3949         /**
3950          * @event drop
3951          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3952          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3953          * implementation that does something to process the drop event and returns true so that the drag source's
3954          * repair action does not run.
3955          * 
3956          * IMPORTANT : it should set this.success
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          "drop" : true
3963     });
3964             
3965      
3966     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3967         this.el.dom, 
3968         this.ddGroup || this.group,
3969         {
3970             isTarget: true,
3971             listeners : listeners || {} 
3972            
3973         
3974         }
3975     );
3976
3977 };
3978
3979 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3980     /**
3981      * @cfg {String} overClass
3982      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3983      */
3984      /**
3985      * @cfg {String} ddGroup
3986      * The drag drop group to handle drop events for
3987      */
3988      
3989     /**
3990      * @cfg {String} dropAllowed
3991      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3992      */
3993     dropAllowed : "x-dd-drop-ok",
3994     /**
3995      * @cfg {String} dropNotAllowed
3996      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3997      */
3998     dropNotAllowed : "x-dd-drop-nodrop",
3999     /**
4000      * @cfg {boolean} success
4001      * set this after drop listener.. 
4002      */
4003     success : false,
4004     /**
4005      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4006      * if the drop point is valid for over/enter..
4007      */
4008     valid : false,
4009     // private
4010     isTarget : true,
4011
4012     // private
4013     isNotifyTarget : true,
4014     
4015     /**
4016      * @hide
4017      */
4018     notifyEnter : function(dd, e, data)
4019     {
4020         this.valid = true;
4021         this.fireEvent('enter', dd, e, data);
4022         if(this.overClass){
4023             this.el.addClass(this.overClass);
4024         }
4025         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4026             this.valid ? this.dropAllowed : this.dropNotAllowed
4027         );
4028     },
4029
4030     /**
4031      * @hide
4032      */
4033     notifyOver : function(dd, e, data)
4034     {
4035         this.valid = true;
4036         this.fireEvent('over', dd, e, data);
4037         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4038             this.valid ? this.dropAllowed : this.dropNotAllowed
4039         );
4040     },
4041
4042     /**
4043      * @hide
4044      */
4045     notifyOut : function(dd, e, data)
4046     {
4047         this.fireEvent('out', dd, e, data);
4048         if(this.overClass){
4049             this.el.removeClass(this.overClass);
4050         }
4051     },
4052
4053     /**
4054      * @hide
4055      */
4056     notifyDrop : function(dd, e, data)
4057     {
4058         this.success = false;
4059         this.fireEvent('drop', dd, e, data);
4060         return this.success;
4061     }
4062 });/*
4063  * Based on:
4064  * Ext JS Library 1.1.1
4065  * Copyright(c) 2006-2007, Ext JS, LLC.
4066  *
4067  * Originally Released Under LGPL - original licence link has changed is not relivant.
4068  *
4069  * Fork - LGPL
4070  * <script type="text/javascript">
4071  */
4072
4073
4074 /**
4075  * @class Roo.dd.DragZone
4076  * @extends Roo.dd.DragSource
4077  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4078  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4079  * @constructor
4080  * @param {String/HTMLElement/Element} el The container element
4081  * @param {Object} config
4082  */
4083 Roo.dd.DragZone = function(el, config){
4084     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4085     if(this.containerScroll){
4086         Roo.dd.ScrollManager.register(this.el);
4087     }
4088 };
4089
4090 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4091     /**
4092      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4093      * for auto scrolling during drag operations.
4094      */
4095     /**
4096      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4097      * method after a failed drop (defaults to "c3daf9" - light blue)
4098      */
4099
4100     /**
4101      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4102      * for a valid target to drag based on the mouse down. Override this method
4103      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4104      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4105      * @param {EventObject} e The mouse down event
4106      * @return {Object} The dragData
4107      */
4108     getDragData : function(e){
4109         return Roo.dd.Registry.getHandleFromEvent(e);
4110     },
4111     
4112     /**
4113      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4114      * this.dragData.ddel
4115      * @param {Number} x The x position of the click on the dragged object
4116      * @param {Number} y The y position of the click on the dragged object
4117      * @return {Boolean} true to continue the drag, false to cancel
4118      */
4119     onInitDrag : function(x, y){
4120         this.proxy.update(this.dragData.ddel.cloneNode(true));
4121         this.onStartDrag(x, y);
4122         return true;
4123     },
4124     
4125     /**
4126      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4127      */
4128     afterRepair : function(){
4129         if(Roo.enableFx){
4130             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4131         }
4132         this.dragging = false;
4133     },
4134
4135     /**
4136      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4137      * the XY of this.dragData.ddel
4138      * @param {EventObject} e The mouse up event
4139      * @return {Array} The xy location (e.g. [100, 200])
4140      */
4141     getRepairXY : function(e){
4142         return Roo.Element.fly(this.dragData.ddel).getXY();  
4143     }
4144 });/*
4145  * Based on:
4146  * Ext JS Library 1.1.1
4147  * Copyright(c) 2006-2007, Ext JS, LLC.
4148  *
4149  * Originally Released Under LGPL - original licence link has changed is not relivant.
4150  *
4151  * Fork - LGPL
4152  * <script type="text/javascript">
4153  */
4154 /**
4155  * @class Roo.dd.DropZone
4156  * @extends Roo.dd.DropTarget
4157  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4158  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4159  * @constructor
4160  * @param {String/HTMLElement/Element} el The container element
4161  * @param {Object} config
4162  */
4163 Roo.dd.DropZone = function(el, config){
4164     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4165 };
4166
4167 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4168     /**
4169      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4170      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4171      * provide your own custom lookup.
4172      * @param {Event} e The event
4173      * @return {Object} data The custom data
4174      */
4175     getTargetFromEvent : function(e){
4176         return Roo.dd.Registry.getTargetFromEvent(e);
4177     },
4178
4179     /**
4180      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4181      * that it has registered.  This method has no default implementation and should be overridden to provide
4182      * node-specific processing if necessary.
4183      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4184      * {@link #getTargetFromEvent} for this node)
4185      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4186      * @param {Event} e The event
4187      * @param {Object} data An object containing arbitrary data supplied by the drag source
4188      */
4189     onNodeEnter : function(n, dd, e, data){
4190         
4191     },
4192
4193     /**
4194      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4195      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4196      * overridden to provide the proper feedback.
4197      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4198      * {@link #getTargetFromEvent} for this node)
4199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4200      * @param {Event} e The event
4201      * @param {Object} data An object containing arbitrary data supplied by the drag source
4202      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4203      * underlying {@link Roo.dd.StatusProxy} can be updated
4204      */
4205     onNodeOver : function(n, dd, e, data){
4206         return this.dropAllowed;
4207     },
4208
4209     /**
4210      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4211      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4212      * node-specific processing if necessary.
4213      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4214      * {@link #getTargetFromEvent} for this node)
4215      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4216      * @param {Event} e The event
4217      * @param {Object} data An object containing arbitrary data supplied by the drag source
4218      */
4219     onNodeOut : function(n, dd, e, data){
4220         
4221     },
4222
4223     /**
4224      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4225      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4226      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4227      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4228      * {@link #getTargetFromEvent} for this node)
4229      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4230      * @param {Event} e The event
4231      * @param {Object} data An object containing arbitrary data supplied by the drag source
4232      * @return {Boolean} True if the drop was valid, else false
4233      */
4234     onNodeDrop : function(n, dd, e, data){
4235         return false;
4236     },
4237
4238     /**
4239      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4240      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4241      * it should be overridden to provide the proper feedback if necessary.
4242      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4243      * @param {Event} e The event
4244      * @param {Object} data An object containing arbitrary data supplied by the drag source
4245      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4246      * underlying {@link Roo.dd.StatusProxy} can be updated
4247      */
4248     onContainerOver : function(dd, e, data){
4249         return this.dropNotAllowed;
4250     },
4251
4252     /**
4253      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4254      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4255      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4256      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
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 {Boolean} True if the drop was valid, else false
4261      */
4262     onContainerDrop : function(dd, e, data){
4263         return false;
4264     },
4265
4266     /**
4267      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4268      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4269      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4270      * you should override this method and provide a custom implementation.
4271      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4272      * @param {Event} e The event
4273      * @param {Object} data An object containing arbitrary data supplied by the drag source
4274      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4275      * underlying {@link Roo.dd.StatusProxy} can be updated
4276      */
4277     notifyEnter : function(dd, e, data){
4278         return this.dropNotAllowed;
4279     },
4280
4281     /**
4282      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4283      * This method will be called on every mouse movement while the drag source is over the drop zone.
4284      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4285      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4286      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4287      * registered node, it will call {@link #onContainerOver}.
4288      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4289      * @param {Event} e The event
4290      * @param {Object} data An object containing arbitrary data supplied by the drag source
4291      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4292      * underlying {@link Roo.dd.StatusProxy} can be updated
4293      */
4294     notifyOver : function(dd, e, data){
4295         var n = this.getTargetFromEvent(e);
4296         if(!n){ // not over valid drop target
4297             if(this.lastOverNode){
4298                 this.onNodeOut(this.lastOverNode, dd, e, data);
4299                 this.lastOverNode = null;
4300             }
4301             return this.onContainerOver(dd, e, data);
4302         }
4303         if(this.lastOverNode != n){
4304             if(this.lastOverNode){
4305                 this.onNodeOut(this.lastOverNode, dd, e, data);
4306             }
4307             this.onNodeEnter(n, dd, e, data);
4308             this.lastOverNode = n;
4309         }
4310         return this.onNodeOver(n, dd, e, data);
4311     },
4312
4313     /**
4314      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4315      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4316      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4318      * @param {Event} e The event
4319      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4320      */
4321     notifyOut : function(dd, e, data){
4322         if(this.lastOverNode){
4323             this.onNodeOut(this.lastOverNode, dd, e, data);
4324             this.lastOverNode = null;
4325         }
4326     },
4327
4328     /**
4329      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4330      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4331      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4332      * otherwise it will call {@link #onContainerDrop}.
4333      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4334      * @param {Event} e The event
4335      * @param {Object} data An object containing arbitrary data supplied by the drag source
4336      * @return {Boolean} True if the drop was valid, else false
4337      */
4338     notifyDrop : function(dd, e, data){
4339         if(this.lastOverNode){
4340             this.onNodeOut(this.lastOverNode, dd, e, data);
4341             this.lastOverNode = null;
4342         }
4343         var n = this.getTargetFromEvent(e);
4344         return n ?
4345             this.onNodeDrop(n, dd, e, data) :
4346             this.onContainerDrop(dd, e, data);
4347     },
4348
4349     // private
4350     triggerCacheRefresh : function(){
4351         Roo.dd.DDM.refreshCache(this.groups);
4352     }  
4353 });/*
4354  * Based on:
4355  * Ext JS Library 1.1.1
4356  * Copyright(c) 2006-2007, Ext JS, LLC.
4357  *
4358  * Originally Released Under LGPL - original licence link has changed is not relivant.
4359  *
4360  * Fork - LGPL
4361  * <script type="text/javascript">
4362  */
4363
4364
4365 /**
4366  * @class Roo.data.SortTypes
4367  * @singleton
4368  * Defines the default sorting (casting?) comparison functions used when sorting data.
4369  */
4370 Roo.data.SortTypes = {
4371     /**
4372      * Default sort that does nothing
4373      * @param {Mixed} s The value being converted
4374      * @return {Mixed} The comparison value
4375      */
4376     none : function(s){
4377         return s;
4378     },
4379     
4380     /**
4381      * The regular expression used to strip tags
4382      * @type {RegExp}
4383      * @property
4384      */
4385     stripTagsRE : /<\/?[^>]+>/gi,
4386     
4387     /**
4388      * Strips all HTML tags to sort on text only
4389      * @param {Mixed} s The value being converted
4390      * @return {String} The comparison value
4391      */
4392     asText : function(s){
4393         return String(s).replace(this.stripTagsRE, "");
4394     },
4395     
4396     /**
4397      * Strips all HTML tags to sort on text only - Case insensitive
4398      * @param {Mixed} s The value being converted
4399      * @return {String} The comparison value
4400      */
4401     asUCText : function(s){
4402         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4403     },
4404     
4405     /**
4406      * Case insensitive string
4407      * @param {Mixed} s The value being converted
4408      * @return {String} The comparison value
4409      */
4410     asUCString : function(s) {
4411         return String(s).toUpperCase();
4412     },
4413     
4414     /**
4415      * Date sorting
4416      * @param {Mixed} s The value being converted
4417      * @return {Number} The comparison value
4418      */
4419     asDate : function(s) {
4420         if(!s){
4421             return 0;
4422         }
4423         if(s instanceof Date){
4424             return s.getTime();
4425         }
4426         return Date.parse(String(s));
4427     },
4428     
4429     /**
4430      * Float sorting
4431      * @param {Mixed} s The value being converted
4432      * @return {Float} The comparison value
4433      */
4434     asFloat : function(s) {
4435         var val = parseFloat(String(s).replace(/,/g, ""));
4436         if(isNaN(val)) val = 0;
4437         return val;
4438     },
4439     
4440     /**
4441      * Integer sorting
4442      * @param {Mixed} s The value being converted
4443      * @return {Number} The comparison value
4444      */
4445     asInt : function(s) {
4446         var val = parseInt(String(s).replace(/,/g, ""));
4447         if(isNaN(val)) val = 0;
4448         return val;
4449     }
4450 };/*
4451  * Based on:
4452  * Ext JS Library 1.1.1
4453  * Copyright(c) 2006-2007, Ext JS, LLC.
4454  *
4455  * Originally Released Under LGPL - original licence link has changed is not relivant.
4456  *
4457  * Fork - LGPL
4458  * <script type="text/javascript">
4459  */
4460
4461 /**
4462 * @class Roo.data.Record
4463  * Instances of this class encapsulate both record <em>definition</em> information, and record
4464  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4465  * to access Records cached in an {@link Roo.data.Store} object.<br>
4466  * <p>
4467  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4468  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4469  * objects.<br>
4470  * <p>
4471  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4472  * @constructor
4473  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4474  * {@link #create}. The parameters are the same.
4475  * @param {Array} data An associative Array of data values keyed by the field name.
4476  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4477  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4478  * not specified an integer id is generated.
4479  */
4480 Roo.data.Record = function(data, id){
4481     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4482     this.data = data;
4483 };
4484
4485 /**
4486  * Generate a constructor for a specific record layout.
4487  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4488  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4489  * Each field definition object may contain the following properties: <ul>
4490  * <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,
4491  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4492  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4493  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4494  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4495  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4496  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4497  * this may be omitted.</p></li>
4498  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4499  * <ul><li>auto (Default, implies no conversion)</li>
4500  * <li>string</li>
4501  * <li>int</li>
4502  * <li>float</li>
4503  * <li>boolean</li>
4504  * <li>date</li></ul></p></li>
4505  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4506  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4507  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4508  * by the Reader into an object that will be stored in the Record. It is passed the
4509  * following parameters:<ul>
4510  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4511  * </ul></p></li>
4512  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4513  * </ul>
4514  * <br>usage:<br><pre><code>
4515 var TopicRecord = Roo.data.Record.create(
4516     {name: 'title', mapping: 'topic_title'},
4517     {name: 'author', mapping: 'username'},
4518     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4519     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4520     {name: 'lastPoster', mapping: 'user2'},
4521     {name: 'excerpt', mapping: 'post_text'}
4522 );
4523
4524 var myNewRecord = new TopicRecord({
4525     title: 'Do my job please',
4526     author: 'noobie',
4527     totalPosts: 1,
4528     lastPost: new Date(),
4529     lastPoster: 'Animal',
4530     excerpt: 'No way dude!'
4531 });
4532 myStore.add(myNewRecord);
4533 </code></pre>
4534  * @method create
4535  * @static
4536  */
4537 Roo.data.Record.create = function(o){
4538     var f = function(){
4539         f.superclass.constructor.apply(this, arguments);
4540     };
4541     Roo.extend(f, Roo.data.Record);
4542     var p = f.prototype;
4543     p.fields = new Roo.util.MixedCollection(false, function(field){
4544         return field.name;
4545     });
4546     for(var i = 0, len = o.length; i < len; i++){
4547         p.fields.add(new Roo.data.Field(o[i]));
4548     }
4549     f.getField = function(name){
4550         return p.fields.get(name);  
4551     };
4552     return f;
4553 };
4554
4555 Roo.data.Record.AUTO_ID = 1000;
4556 Roo.data.Record.EDIT = 'edit';
4557 Roo.data.Record.REJECT = 'reject';
4558 Roo.data.Record.COMMIT = 'commit';
4559
4560 Roo.data.Record.prototype = {
4561     /**
4562      * Readonly flag - true if this record has been modified.
4563      * @type Boolean
4564      */
4565     dirty : false,
4566     editing : false,
4567     error: null,
4568     modified: null,
4569
4570     // private
4571     join : function(store){
4572         this.store = store;
4573     },
4574
4575     /**
4576      * Set the named field to the specified value.
4577      * @param {String} name The name of the field to set.
4578      * @param {Object} value The value to set the field to.
4579      */
4580     set : function(name, value){
4581         if(this.data[name] == value){
4582             return;
4583         }
4584         this.dirty = true;
4585         if(!this.modified){
4586             this.modified = {};
4587         }
4588         if(typeof this.modified[name] == 'undefined'){
4589             this.modified[name] = this.data[name];
4590         }
4591         this.data[name] = value;
4592         if(!this.editing && this.store){
4593             this.store.afterEdit(this);
4594         }       
4595     },
4596
4597     /**
4598      * Get the value of the named field.
4599      * @param {String} name The name of the field to get the value of.
4600      * @return {Object} The value of the field.
4601      */
4602     get : function(name){
4603         return this.data[name]; 
4604     },
4605
4606     // private
4607     beginEdit : function(){
4608         this.editing = true;
4609         this.modified = {}; 
4610     },
4611
4612     // private
4613     cancelEdit : function(){
4614         this.editing = false;
4615         delete this.modified;
4616     },
4617
4618     // private
4619     endEdit : function(){
4620         this.editing = false;
4621         if(this.dirty && this.store){
4622             this.store.afterEdit(this);
4623         }
4624     },
4625
4626     /**
4627      * Usually called by the {@link Roo.data.Store} which owns the Record.
4628      * Rejects all changes made to the Record since either creation, or the last commit operation.
4629      * Modified fields are reverted to their original values.
4630      * <p>
4631      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4632      * of reject operations.
4633      */
4634     reject : function(){
4635         var m = this.modified;
4636         for(var n in m){
4637             if(typeof m[n] != "function"){
4638                 this.data[n] = m[n];
4639             }
4640         }
4641         this.dirty = false;
4642         delete this.modified;
4643         this.editing = false;
4644         if(this.store){
4645             this.store.afterReject(this);
4646         }
4647     },
4648
4649     /**
4650      * Usually called by the {@link Roo.data.Store} which owns the Record.
4651      * Commits all changes made to the Record since either creation, or the last commit operation.
4652      * <p>
4653      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4654      * of commit operations.
4655      */
4656     commit : function(){
4657         this.dirty = false;
4658         delete this.modified;
4659         this.editing = false;
4660         if(this.store){
4661             this.store.afterCommit(this);
4662         }
4663     },
4664
4665     // private
4666     hasError : function(){
4667         return this.error != null;
4668     },
4669
4670     // private
4671     clearError : function(){
4672         this.error = null;
4673     },
4674
4675     /**
4676      * Creates a copy of this record.
4677      * @param {String} id (optional) A new record id if you don't want to use this record's id
4678      * @return {Record}
4679      */
4680     copy : function(newId) {
4681         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4682     }
4683 };/*
4684  * Based on:
4685  * Ext JS Library 1.1.1
4686  * Copyright(c) 2006-2007, Ext JS, LLC.
4687  *
4688  * Originally Released Under LGPL - original licence link has changed is not relivant.
4689  *
4690  * Fork - LGPL
4691  * <script type="text/javascript">
4692  */
4693
4694
4695
4696 /**
4697  * @class Roo.data.Store
4698  * @extends Roo.util.Observable
4699  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4700  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4701  * <p>
4702  * 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
4703  * has no knowledge of the format of the data returned by the Proxy.<br>
4704  * <p>
4705  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4706  * instances from the data object. These records are cached and made available through accessor functions.
4707  * @constructor
4708  * Creates a new Store.
4709  * @param {Object} config A config object containing the objects needed for the Store to access data,
4710  * and read the data into Records.
4711  */
4712 Roo.data.Store = function(config){
4713     this.data = new Roo.util.MixedCollection(false);
4714     this.data.getKey = function(o){
4715         return o.id;
4716     };
4717     this.baseParams = {};
4718     // private
4719     this.paramNames = {
4720         "start" : "start",
4721         "limit" : "limit",
4722         "sort" : "sort",
4723         "dir" : "dir",
4724         "multisort" : "_multisort"
4725     };
4726
4727     if(config && config.data){
4728         this.inlineData = config.data;
4729         delete config.data;
4730     }
4731
4732     Roo.apply(this, config);
4733     
4734     if(this.reader){ // reader passed
4735         this.reader = Roo.factory(this.reader, Roo.data);
4736         this.reader.xmodule = this.xmodule || false;
4737         if(!this.recordType){
4738             this.recordType = this.reader.recordType;
4739         }
4740         if(this.reader.onMetaChange){
4741             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4742         }
4743     }
4744
4745     if(this.recordType){
4746         this.fields = this.recordType.prototype.fields;
4747     }
4748     this.modified = [];
4749
4750     this.addEvents({
4751         /**
4752          * @event datachanged
4753          * Fires when the data cache has changed, and a widget which is using this Store
4754          * as a Record cache should refresh its view.
4755          * @param {Store} this
4756          */
4757         datachanged : true,
4758         /**
4759          * @event metachange
4760          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4761          * @param {Store} this
4762          * @param {Object} meta The JSON metadata
4763          */
4764         metachange : true,
4765         /**
4766          * @event add
4767          * Fires when Records have been added to the Store
4768          * @param {Store} this
4769          * @param {Roo.data.Record[]} records The array of Records added
4770          * @param {Number} index The index at which the record(s) were added
4771          */
4772         add : true,
4773         /**
4774          * @event remove
4775          * Fires when a Record has been removed from the Store
4776          * @param {Store} this
4777          * @param {Roo.data.Record} record The Record that was removed
4778          * @param {Number} index The index at which the record was removed
4779          */
4780         remove : true,
4781         /**
4782          * @event update
4783          * Fires when a Record has been updated
4784          * @param {Store} this
4785          * @param {Roo.data.Record} record The Record that was updated
4786          * @param {String} operation The update operation being performed.  Value may be one of:
4787          * <pre><code>
4788  Roo.data.Record.EDIT
4789  Roo.data.Record.REJECT
4790  Roo.data.Record.COMMIT
4791          * </code></pre>
4792          */
4793         update : true,
4794         /**
4795          * @event clear
4796          * Fires when the data cache has been cleared.
4797          * @param {Store} this
4798          */
4799         clear : true,
4800         /**
4801          * @event beforeload
4802          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4803          * the load action will be canceled.
4804          * @param {Store} this
4805          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4806          */
4807         beforeload : true,
4808         /**
4809          * @event load
4810          * Fires after a new set of Records has been loaded.
4811          * @param {Store} this
4812          * @param {Roo.data.Record[]} records The Records that were loaded
4813          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4814          */
4815         load : true,
4816         /**
4817          * @event loadexception
4818          * Fires if an exception occurs in the Proxy during loading.
4819          * Called with the signature of the Proxy's "loadexception" event.
4820          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4821          * 
4822          * @param {Proxy} 
4823          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4824          * @param {Object} load options 
4825          * @param {Object} jsonData from your request (normally this contains the Exception)
4826          */
4827         loadexception : true
4828     });
4829     
4830     if(this.proxy){
4831         this.proxy = Roo.factory(this.proxy, Roo.data);
4832         this.proxy.xmodule = this.xmodule || false;
4833         this.relayEvents(this.proxy,  ["loadexception"]);
4834     }
4835     this.sortToggle = {};
4836     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4837
4838     Roo.data.Store.superclass.constructor.call(this);
4839
4840     if(this.inlineData){
4841         this.loadData(this.inlineData);
4842         delete this.inlineData;
4843     }
4844 };
4845 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4846      /**
4847     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4848     * without a remote query - used by combo/forms at present.
4849     */
4850     
4851     /**
4852     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4853     */
4854     /**
4855     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4856     */
4857     /**
4858     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4859     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4860     */
4861     /**
4862     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4863     * on any HTTP request
4864     */
4865     /**
4866     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4867     */
4868     /**
4869     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4870     */
4871     multiSort: false,
4872     /**
4873     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4874     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4875     */
4876     remoteSort : false,
4877
4878     /**
4879     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4880      * loaded or when a record is removed. (defaults to false).
4881     */
4882     pruneModifiedRecords : false,
4883
4884     // private
4885     lastOptions : null,
4886
4887     /**
4888      * Add Records to the Store and fires the add event.
4889      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4890      */
4891     add : function(records){
4892         records = [].concat(records);
4893         for(var i = 0, len = records.length; i < len; i++){
4894             records[i].join(this);
4895         }
4896         var index = this.data.length;
4897         this.data.addAll(records);
4898         this.fireEvent("add", this, records, index);
4899     },
4900
4901     /**
4902      * Remove a Record from the Store and fires the remove event.
4903      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4904      */
4905     remove : function(record){
4906         var index = this.data.indexOf(record);
4907         this.data.removeAt(index);
4908         if(this.pruneModifiedRecords){
4909             this.modified.remove(record);
4910         }
4911         this.fireEvent("remove", this, record, index);
4912     },
4913
4914     /**
4915      * Remove all Records from the Store and fires the clear event.
4916      */
4917     removeAll : function(){
4918         this.data.clear();
4919         if(this.pruneModifiedRecords){
4920             this.modified = [];
4921         }
4922         this.fireEvent("clear", this);
4923     },
4924
4925     /**
4926      * Inserts Records to the Store at the given index and fires the add event.
4927      * @param {Number} index The start index at which to insert the passed Records.
4928      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4929      */
4930     insert : function(index, records){
4931         records = [].concat(records);
4932         for(var i = 0, len = records.length; i < len; i++){
4933             this.data.insert(index, records[i]);
4934             records[i].join(this);
4935         }
4936         this.fireEvent("add", this, records, index);
4937     },
4938
4939     /**
4940      * Get the index within the cache of the passed Record.
4941      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4942      * @return {Number} The index of the passed Record. Returns -1 if not found.
4943      */
4944     indexOf : function(record){
4945         return this.data.indexOf(record);
4946     },
4947
4948     /**
4949      * Get the index within the cache of the Record with the passed id.
4950      * @param {String} id The id of the Record to find.
4951      * @return {Number} The index of the Record. Returns -1 if not found.
4952      */
4953     indexOfId : function(id){
4954         return this.data.indexOfKey(id);
4955     },
4956
4957     /**
4958      * Get the Record with the specified id.
4959      * @param {String} id The id of the Record to find.
4960      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4961      */
4962     getById : function(id){
4963         return this.data.key(id);
4964     },
4965
4966     /**
4967      * Get the Record at the specified index.
4968      * @param {Number} index The index of the Record to find.
4969      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4970      */
4971     getAt : function(index){
4972         return this.data.itemAt(index);
4973     },
4974
4975     /**
4976      * Returns a range of Records between specified indices.
4977      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4978      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4979      * @return {Roo.data.Record[]} An array of Records
4980      */
4981     getRange : function(start, end){
4982         return this.data.getRange(start, end);
4983     },
4984
4985     // private
4986     storeOptions : function(o){
4987         o = Roo.apply({}, o);
4988         delete o.callback;
4989         delete o.scope;
4990         this.lastOptions = o;
4991     },
4992
4993     /**
4994      * Loads the Record cache from the configured Proxy using the configured Reader.
4995      * <p>
4996      * If using remote paging, then the first load call must specify the <em>start</em>
4997      * and <em>limit</em> properties in the options.params property to establish the initial
4998      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4999      * <p>
5000      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5001      * and this call will return before the new data has been loaded. Perform any post-processing
5002      * in a callback function, or in a "load" event handler.</strong>
5003      * <p>
5004      * @param {Object} options An object containing properties which control loading options:<ul>
5005      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5006      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5007      * passed the following arguments:<ul>
5008      * <li>r : Roo.data.Record[]</li>
5009      * <li>options: Options object from the load call</li>
5010      * <li>success: Boolean success indicator</li></ul></li>
5011      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5012      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5013      * </ul>
5014      */
5015     load : function(options){
5016         options = options || {};
5017         if(this.fireEvent("beforeload", this, options) !== false){
5018             this.storeOptions(options);
5019             var p = Roo.apply(options.params || {}, this.baseParams);
5020             // if meta was not loaded from remote source.. try requesting it.
5021             if (!this.reader.metaFromRemote) {
5022                 p._requestMeta = 1;
5023             }
5024             if(this.sortInfo && this.remoteSort){
5025                 var pn = this.paramNames;
5026                 p[pn["sort"]] = this.sortInfo.field;
5027                 p[pn["dir"]] = this.sortInfo.direction;
5028             }
5029             if (this.multiSort) {
5030                 var pn = this.paramNames;
5031                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5032             }
5033             
5034             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5035         }
5036     },
5037
5038     /**
5039      * Reloads the Record cache from the configured Proxy using the configured Reader and
5040      * the options from the last load operation performed.
5041      * @param {Object} options (optional) An object containing properties which may override the options
5042      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5043      * the most recently used options are reused).
5044      */
5045     reload : function(options){
5046         this.load(Roo.applyIf(options||{}, this.lastOptions));
5047     },
5048
5049     // private
5050     // Called as a callback by the Reader during a load operation.
5051     loadRecords : function(o, options, success){
5052         if(!o || success === false){
5053             if(success !== false){
5054                 this.fireEvent("load", this, [], options);
5055             }
5056             if(options.callback){
5057                 options.callback.call(options.scope || this, [], options, false);
5058             }
5059             return;
5060         }
5061         // if data returned failure - throw an exception.
5062         if (o.success === false) {
5063             // show a message if no listener is registered.
5064             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
5065                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
5066             }
5067             // loadmask wil be hooked into this..
5068             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5069             return;
5070         }
5071         var r = o.records, t = o.totalRecords || r.length;
5072         if(!options || options.add !== true){
5073             if(this.pruneModifiedRecords){
5074                 this.modified = [];
5075             }
5076             for(var i = 0, len = r.length; i < len; i++){
5077                 r[i].join(this);
5078             }
5079             if(this.snapshot){
5080                 this.data = this.snapshot;
5081                 delete this.snapshot;
5082             }
5083             this.data.clear();
5084             this.data.addAll(r);
5085             this.totalLength = t;
5086             this.applySort();
5087             this.fireEvent("datachanged", this);
5088         }else{
5089             this.totalLength = Math.max(t, this.data.length+r.length);
5090             this.add(r);
5091         }
5092         this.fireEvent("load", this, r, options);
5093         if(options.callback){
5094             options.callback.call(options.scope || this, r, options, true);
5095         }
5096     },
5097
5098
5099     /**
5100      * Loads data from a passed data block. A Reader which understands the format of the data
5101      * must have been configured in the constructor.
5102      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5103      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5104      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5105      */
5106     loadData : function(o, append){
5107         var r = this.reader.readRecords(o);
5108         this.loadRecords(r, {add: append}, true);
5109     },
5110
5111     /**
5112      * Gets the number of cached records.
5113      * <p>
5114      * <em>If using paging, this may not be the total size of the dataset. If the data object
5115      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5116      * the data set size</em>
5117      */
5118     getCount : function(){
5119         return this.data.length || 0;
5120     },
5121
5122     /**
5123      * Gets the total number of records in the dataset as returned by the server.
5124      * <p>
5125      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5126      * the dataset size</em>
5127      */
5128     getTotalCount : function(){
5129         return this.totalLength || 0;
5130     },
5131
5132     /**
5133      * Returns the sort state of the Store as an object with two properties:
5134      * <pre><code>
5135  field {String} The name of the field by which the Records are sorted
5136  direction {String} The sort order, "ASC" or "DESC"
5137      * </code></pre>
5138      */
5139     getSortState : function(){
5140         return this.sortInfo;
5141     },
5142
5143     // private
5144     applySort : function(){
5145         if(this.sortInfo && !this.remoteSort){
5146             var s = this.sortInfo, f = s.field;
5147             var st = this.fields.get(f).sortType;
5148             var fn = function(r1, r2){
5149                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5150                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5151             };
5152             this.data.sort(s.direction, fn);
5153             if(this.snapshot && this.snapshot != this.data){
5154                 this.snapshot.sort(s.direction, fn);
5155             }
5156         }
5157     },
5158
5159     /**
5160      * Sets the default sort column and order to be used by the next load operation.
5161      * @param {String} fieldName The name of the field to sort by.
5162      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5163      */
5164     setDefaultSort : function(field, dir){
5165         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5166     },
5167
5168     /**
5169      * Sort the Records.
5170      * If remote sorting is used, the sort is performed on the server, and the cache is
5171      * reloaded. If local sorting is used, the cache is sorted internally.
5172      * @param {String} fieldName The name of the field to sort by.
5173      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5174      */
5175     sort : function(fieldName, dir){
5176         var f = this.fields.get(fieldName);
5177         if(!dir){
5178             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5179             
5180             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5181                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5182             }else{
5183                 dir = f.sortDir;
5184             }
5185         }
5186         this.sortToggle[f.name] = dir;
5187         this.sortInfo = {field: f.name, direction: dir};
5188         if(!this.remoteSort){
5189             this.applySort();
5190             this.fireEvent("datachanged", this);
5191         }else{
5192             this.load(this.lastOptions);
5193         }
5194     },
5195
5196     /**
5197      * Calls the specified function for each of the Records in the cache.
5198      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5199      * Returning <em>false</em> aborts and exits the iteration.
5200      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5201      */
5202     each : function(fn, scope){
5203         this.data.each(fn, scope);
5204     },
5205
5206     /**
5207      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5208      * (e.g., during paging).
5209      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5210      */
5211     getModifiedRecords : function(){
5212         return this.modified;
5213     },
5214
5215     // private
5216     createFilterFn : function(property, value, anyMatch){
5217         if(!value.exec){ // not a regex
5218             value = String(value);
5219             if(value.length == 0){
5220                 return false;
5221             }
5222             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5223         }
5224         return function(r){
5225             return value.test(r.data[property]);
5226         };
5227     },
5228
5229     /**
5230      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5231      * @param {String} property A field on your records
5232      * @param {Number} start The record index to start at (defaults to 0)
5233      * @param {Number} end The last record index to include (defaults to length - 1)
5234      * @return {Number} The sum
5235      */
5236     sum : function(property, start, end){
5237         var rs = this.data.items, v = 0;
5238         start = start || 0;
5239         end = (end || end === 0) ? end : rs.length-1;
5240
5241         for(var i = start; i <= end; i++){
5242             v += (rs[i].data[property] || 0);
5243         }
5244         return v;
5245     },
5246
5247     /**
5248      * Filter the records by a specified property.
5249      * @param {String} field A field on your records
5250      * @param {String/RegExp} value Either a string that the field
5251      * should start with or a RegExp to test against the field
5252      * @param {Boolean} anyMatch True to match any part not just the beginning
5253      */
5254     filter : function(property, value, anyMatch){
5255         var fn = this.createFilterFn(property, value, anyMatch);
5256         return fn ? this.filterBy(fn) : this.clearFilter();
5257     },
5258
5259     /**
5260      * Filter by a function. The specified function will be called with each
5261      * record in this data source. If the function returns true the record is included,
5262      * otherwise it is filtered.
5263      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5264      * @param {Object} scope (optional) The scope of the function (defaults to this)
5265      */
5266     filterBy : function(fn, scope){
5267         this.snapshot = this.snapshot || this.data;
5268         this.data = this.queryBy(fn, scope||this);
5269         this.fireEvent("datachanged", this);
5270     },
5271
5272     /**
5273      * Query the records by a specified property.
5274      * @param {String} field A field on your records
5275      * @param {String/RegExp} value Either a string that the field
5276      * should start with or a RegExp to test against the field
5277      * @param {Boolean} anyMatch True to match any part not just the beginning
5278      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5279      */
5280     query : function(property, value, anyMatch){
5281         var fn = this.createFilterFn(property, value, anyMatch);
5282         return fn ? this.queryBy(fn) : this.data.clone();
5283     },
5284
5285     /**
5286      * Query by a function. The specified function will be called with each
5287      * record in this data source. If the function returns true the record is included
5288      * in the results.
5289      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5290      * @param {Object} scope (optional) The scope of the function (defaults to this)
5291       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5292      **/
5293     queryBy : function(fn, scope){
5294         var data = this.snapshot || this.data;
5295         return data.filterBy(fn, scope||this);
5296     },
5297
5298     /**
5299      * Collects unique values for a particular dataIndex from this store.
5300      * @param {String} dataIndex The property to collect
5301      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5302      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5303      * @return {Array} An array of the unique values
5304      **/
5305     collect : function(dataIndex, allowNull, bypassFilter){
5306         var d = (bypassFilter === true && this.snapshot) ?
5307                 this.snapshot.items : this.data.items;
5308         var v, sv, r = [], l = {};
5309         for(var i = 0, len = d.length; i < len; i++){
5310             v = d[i].data[dataIndex];
5311             sv = String(v);
5312             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5313                 l[sv] = true;
5314                 r[r.length] = v;
5315             }
5316         }
5317         return r;
5318     },
5319
5320     /**
5321      * Revert to a view of the Record cache with no filtering applied.
5322      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5323      */
5324     clearFilter : function(suppressEvent){
5325         if(this.snapshot && this.snapshot != this.data){
5326             this.data = this.snapshot;
5327             delete this.snapshot;
5328             if(suppressEvent !== true){
5329                 this.fireEvent("datachanged", this);
5330             }
5331         }
5332     },
5333
5334     // private
5335     afterEdit : function(record){
5336         if(this.modified.indexOf(record) == -1){
5337             this.modified.push(record);
5338         }
5339         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5340     },
5341     
5342     // private
5343     afterReject : function(record){
5344         this.modified.remove(record);
5345         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5346     },
5347
5348     // private
5349     afterCommit : function(record){
5350         this.modified.remove(record);
5351         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5352     },
5353
5354     /**
5355      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5356      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5357      */
5358     commitChanges : function(){
5359         var m = this.modified.slice(0);
5360         this.modified = [];
5361         for(var i = 0, len = m.length; i < len; i++){
5362             m[i].commit();
5363         }
5364     },
5365
5366     /**
5367      * Cancel outstanding changes on all changed records.
5368      */
5369     rejectChanges : function(){
5370         var m = this.modified.slice(0);
5371         this.modified = [];
5372         for(var i = 0, len = m.length; i < len; i++){
5373             m[i].reject();
5374         }
5375     },
5376
5377     onMetaChange : function(meta, rtype, o){
5378         this.recordType = rtype;
5379         this.fields = rtype.prototype.fields;
5380         delete this.snapshot;
5381         this.sortInfo = meta.sortInfo || this.sortInfo;
5382         this.modified = [];
5383         this.fireEvent('metachange', this, this.reader.meta);
5384     }
5385 });/*
5386  * Based on:
5387  * Ext JS Library 1.1.1
5388  * Copyright(c) 2006-2007, Ext JS, LLC.
5389  *
5390  * Originally Released Under LGPL - original licence link has changed is not relivant.
5391  *
5392  * Fork - LGPL
5393  * <script type="text/javascript">
5394  */
5395
5396 /**
5397  * @class Roo.data.SimpleStore
5398  * @extends Roo.data.Store
5399  * Small helper class to make creating Stores from Array data easier.
5400  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5401  * @cfg {Array} fields An array of field definition objects, or field name strings.
5402  * @cfg {Array} data The multi-dimensional array of data
5403  * @constructor
5404  * @param {Object} config
5405  */
5406 Roo.data.SimpleStore = function(config){
5407     Roo.data.SimpleStore.superclass.constructor.call(this, {
5408         isLocal : true,
5409         reader: new Roo.data.ArrayReader({
5410                 id: config.id
5411             },
5412             Roo.data.Record.create(config.fields)
5413         ),
5414         proxy : new Roo.data.MemoryProxy(config.data)
5415     });
5416     this.load();
5417 };
5418 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5419  * Based on:
5420  * Ext JS Library 1.1.1
5421  * Copyright(c) 2006-2007, Ext JS, LLC.
5422  *
5423  * Originally Released Under LGPL - original licence link has changed is not relivant.
5424  *
5425  * Fork - LGPL
5426  * <script type="text/javascript">
5427  */
5428
5429 /**
5430 /**
5431  * @extends Roo.data.Store
5432  * @class Roo.data.JsonStore
5433  * Small helper class to make creating Stores for JSON data easier. <br/>
5434 <pre><code>
5435 var store = new Roo.data.JsonStore({
5436     url: 'get-images.php',
5437     root: 'images',
5438     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5439 });
5440 </code></pre>
5441  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5442  * JsonReader and HttpProxy (unless inline data is provided).</b>
5443  * @cfg {Array} fields An array of field definition objects, or field name strings.
5444  * @constructor
5445  * @param {Object} config
5446  */
5447 Roo.data.JsonStore = function(c){
5448     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5449         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5450         reader: new Roo.data.JsonReader(c, c.fields)
5451     }));
5452 };
5453 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5454  * Based on:
5455  * Ext JS Library 1.1.1
5456  * Copyright(c) 2006-2007, Ext JS, LLC.
5457  *
5458  * Originally Released Under LGPL - original licence link has changed is not relivant.
5459  *
5460  * Fork - LGPL
5461  * <script type="text/javascript">
5462  */
5463
5464  
5465 Roo.data.Field = function(config){
5466     if(typeof config == "string"){
5467         config = {name: config};
5468     }
5469     Roo.apply(this, config);
5470     
5471     if(!this.type){
5472         this.type = "auto";
5473     }
5474     
5475     var st = Roo.data.SortTypes;
5476     // named sortTypes are supported, here we look them up
5477     if(typeof this.sortType == "string"){
5478         this.sortType = st[this.sortType];
5479     }
5480     
5481     // set default sortType for strings and dates
5482     if(!this.sortType){
5483         switch(this.type){
5484             case "string":
5485                 this.sortType = st.asUCString;
5486                 break;
5487             case "date":
5488                 this.sortType = st.asDate;
5489                 break;
5490             default:
5491                 this.sortType = st.none;
5492         }
5493     }
5494
5495     // define once
5496     var stripRe = /[\$,%]/g;
5497
5498     // prebuilt conversion function for this field, instead of
5499     // switching every time we're reading a value
5500     if(!this.convert){
5501         var cv, dateFormat = this.dateFormat;
5502         switch(this.type){
5503             case "":
5504             case "auto":
5505             case undefined:
5506                 cv = function(v){ return v; };
5507                 break;
5508             case "string":
5509                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5510                 break;
5511             case "int":
5512                 cv = function(v){
5513                     return v !== undefined && v !== null && v !== '' ?
5514                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5515                     };
5516                 break;
5517             case "float":
5518                 cv = function(v){
5519                     return v !== undefined && v !== null && v !== '' ?
5520                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5521                     };
5522                 break;
5523             case "bool":
5524             case "boolean":
5525                 cv = function(v){ return v === true || v === "true" || v == 1; };
5526                 break;
5527             case "date":
5528                 cv = function(v){
5529                     if(!v){
5530                         return '';
5531                     }
5532                     if(v instanceof Date){
5533                         return v;
5534                     }
5535                     if(dateFormat){
5536                         if(dateFormat == "timestamp"){
5537                             return new Date(v*1000);
5538                         }
5539                         return Date.parseDate(v, dateFormat);
5540                     }
5541                     var parsed = Date.parse(v);
5542                     return parsed ? new Date(parsed) : null;
5543                 };
5544              break;
5545             
5546         }
5547         this.convert = cv;
5548     }
5549 };
5550
5551 Roo.data.Field.prototype = {
5552     dateFormat: null,
5553     defaultValue: "",
5554     mapping: null,
5555     sortType : null,
5556     sortDir : "ASC"
5557 };/*
5558  * Based on:
5559  * Ext JS Library 1.1.1
5560  * Copyright(c) 2006-2007, Ext JS, LLC.
5561  *
5562  * Originally Released Under LGPL - original licence link has changed is not relivant.
5563  *
5564  * Fork - LGPL
5565  * <script type="text/javascript">
5566  */
5567  
5568 // Base class for reading structured data from a data source.  This class is intended to be
5569 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5570
5571 /**
5572  * @class Roo.data.DataReader
5573  * Base class for reading structured data from a data source.  This class is intended to be
5574  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5575  */
5576
5577 Roo.data.DataReader = function(meta, recordType){
5578     
5579     this.meta = meta;
5580     
5581     this.recordType = recordType instanceof Array ? 
5582         Roo.data.Record.create(recordType) : recordType;
5583 };
5584
5585 Roo.data.DataReader.prototype = {
5586      /**
5587      * Create an empty record
5588      * @param {Object} data (optional) - overlay some values
5589      * @return {Roo.data.Record} record created.
5590      */
5591     newRow :  function(d) {
5592         var da =  {};
5593         this.recordType.prototype.fields.each(function(c) {
5594             switch( c.type) {
5595                 case 'int' : da[c.name] = 0; break;
5596                 case 'date' : da[c.name] = new Date(); break;
5597                 case 'float' : da[c.name] = 0.0; break;
5598                 case 'boolean' : da[c.name] = false; break;
5599                 default : da[c.name] = ""; break;
5600             }
5601             
5602         });
5603         return new this.recordType(Roo.apply(da, d));
5604     }
5605     
5606 };/*
5607  * Based on:
5608  * Ext JS Library 1.1.1
5609  * Copyright(c) 2006-2007, Ext JS, LLC.
5610  *
5611  * Originally Released Under LGPL - original licence link has changed is not relivant.
5612  *
5613  * Fork - LGPL
5614  * <script type="text/javascript">
5615  */
5616
5617 /**
5618  * @class Roo.data.DataProxy
5619  * @extends Roo.data.Observable
5620  * This class is an abstract base class for implementations which provide retrieval of
5621  * unformatted data objects.<br>
5622  * <p>
5623  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5624  * (of the appropriate type which knows how to parse the data object) to provide a block of
5625  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5626  * <p>
5627  * Custom implementations must implement the load method as described in
5628  * {@link Roo.data.HttpProxy#load}.
5629  */
5630 Roo.data.DataProxy = function(){
5631     this.addEvents({
5632         /**
5633          * @event beforeload
5634          * Fires before a network request is made to retrieve a data object.
5635          * @param {Object} This DataProxy object.
5636          * @param {Object} params The params parameter to the load function.
5637          */
5638         beforeload : true,
5639         /**
5640          * @event load
5641          * Fires before the load method's callback is called.
5642          * @param {Object} This DataProxy object.
5643          * @param {Object} o The data object.
5644          * @param {Object} arg The callback argument object passed to the load function.
5645          */
5646         load : true,
5647         /**
5648          * @event loadexception
5649          * Fires if an Exception occurs during data retrieval.
5650          * @param {Object} This DataProxy object.
5651          * @param {Object} o The data object.
5652          * @param {Object} arg The callback argument object passed to the load function.
5653          * @param {Object} e The Exception.
5654          */
5655         loadexception : true
5656     });
5657     Roo.data.DataProxy.superclass.constructor.call(this);
5658 };
5659
5660 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5661
5662     /**
5663      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5664      */
5665 /*
5666  * Based on:
5667  * Ext JS Library 1.1.1
5668  * Copyright(c) 2006-2007, Ext JS, LLC.
5669  *
5670  * Originally Released Under LGPL - original licence link has changed is not relivant.
5671  *
5672  * Fork - LGPL
5673  * <script type="text/javascript">
5674  */
5675 /**
5676  * @class Roo.data.MemoryProxy
5677  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5678  * to the Reader when its load method is called.
5679  * @constructor
5680  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5681  */
5682 Roo.data.MemoryProxy = function(data){
5683     if (data.data) {
5684         data = data.data;
5685     }
5686     Roo.data.MemoryProxy.superclass.constructor.call(this);
5687     this.data = data;
5688 };
5689
5690 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5691     /**
5692      * Load data from the requested source (in this case an in-memory
5693      * data object passed to the constructor), read the data object into
5694      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5695      * process that block using the passed callback.
5696      * @param {Object} params This parameter is not used by the MemoryProxy class.
5697      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5698      * object into a block of Roo.data.Records.
5699      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5700      * The function must be passed <ul>
5701      * <li>The Record block object</li>
5702      * <li>The "arg" argument from the load function</li>
5703      * <li>A boolean success indicator</li>
5704      * </ul>
5705      * @param {Object} scope The scope in which to call the callback
5706      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5707      */
5708     load : function(params, reader, callback, scope, arg){
5709         params = params || {};
5710         var result;
5711         try {
5712             result = reader.readRecords(this.data);
5713         }catch(e){
5714             this.fireEvent("loadexception", this, arg, null, e);
5715             callback.call(scope, null, arg, false);
5716             return;
5717         }
5718         callback.call(scope, result, arg, true);
5719     },
5720     
5721     // private
5722     update : function(params, records){
5723         
5724     }
5725 });/*
5726  * Based on:
5727  * Ext JS Library 1.1.1
5728  * Copyright(c) 2006-2007, Ext JS, LLC.
5729  *
5730  * Originally Released Under LGPL - original licence link has changed is not relivant.
5731  *
5732  * Fork - LGPL
5733  * <script type="text/javascript">
5734  */
5735 /**
5736  * @class Roo.data.HttpProxy
5737  * @extends Roo.data.DataProxy
5738  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5739  * configured to reference a certain URL.<br><br>
5740  * <p>
5741  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5742  * from which the running page was served.<br><br>
5743  * <p>
5744  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5745  * <p>
5746  * Be aware that to enable the browser to parse an XML document, the server must set
5747  * the Content-Type header in the HTTP response to "text/xml".
5748  * @constructor
5749  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5750  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5751  * will be used to make the request.
5752  */
5753 Roo.data.HttpProxy = function(conn){
5754     Roo.data.HttpProxy.superclass.constructor.call(this);
5755     // is conn a conn config or a real conn?
5756     this.conn = conn;
5757     this.useAjax = !conn || !conn.events;
5758   
5759 };
5760
5761 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5762     // thse are take from connection...
5763     
5764     /**
5765      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5766      */
5767     /**
5768      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5769      * extra parameters to each request made by this object. (defaults to undefined)
5770      */
5771     /**
5772      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5773      *  to each request made by this object. (defaults to undefined)
5774      */
5775     /**
5776      * @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)
5777      */
5778     /**
5779      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5780      */
5781      /**
5782      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5783      * @type Boolean
5784      */
5785   
5786
5787     /**
5788      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5789      * @type Boolean
5790      */
5791     /**
5792      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5793      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5794      * a finer-grained basis than the DataProxy events.
5795      */
5796     getConnection : function(){
5797         return this.useAjax ? Roo.Ajax : this.conn;
5798     },
5799
5800     /**
5801      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5802      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5803      * process that block using the passed callback.
5804      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5805      * for the request to the remote server.
5806      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5807      * object into a block of Roo.data.Records.
5808      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5809      * The function must be passed <ul>
5810      * <li>The Record block object</li>
5811      * <li>The "arg" argument from the load function</li>
5812      * <li>A boolean success indicator</li>
5813      * </ul>
5814      * @param {Object} scope The scope in which to call the callback
5815      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5816      */
5817     load : function(params, reader, callback, scope, arg){
5818         if(this.fireEvent("beforeload", this, params) !== false){
5819             var  o = {
5820                 params : params || {},
5821                 request: {
5822                     callback : callback,
5823                     scope : scope,
5824                     arg : arg
5825                 },
5826                 reader: reader,
5827                 callback : this.loadResponse,
5828                 scope: this
5829             };
5830             if(this.useAjax){
5831                 Roo.applyIf(o, this.conn);
5832                 if(this.activeRequest){
5833                     Roo.Ajax.abort(this.activeRequest);
5834                 }
5835                 this.activeRequest = Roo.Ajax.request(o);
5836             }else{
5837                 this.conn.request(o);
5838             }
5839         }else{
5840             callback.call(scope||this, null, arg, false);
5841         }
5842     },
5843
5844     // private
5845     loadResponse : function(o, success, response){
5846         delete this.activeRequest;
5847         if(!success){
5848             this.fireEvent("loadexception", this, o, response);
5849             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5850             return;
5851         }
5852         var result;
5853         try {
5854             result = o.reader.read(response);
5855         }catch(e){
5856             this.fireEvent("loadexception", this, o, response, e);
5857             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5858             return;
5859         }
5860         
5861         this.fireEvent("load", this, o, o.request.arg);
5862         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5863     },
5864
5865     // private
5866     update : function(dataSet){
5867
5868     },
5869
5870     // private
5871     updateResponse : function(dataSet){
5872
5873     }
5874 });/*
5875  * Based on:
5876  * Ext JS Library 1.1.1
5877  * Copyright(c) 2006-2007, Ext JS, LLC.
5878  *
5879  * Originally Released Under LGPL - original licence link has changed is not relivant.
5880  *
5881  * Fork - LGPL
5882  * <script type="text/javascript">
5883  */
5884
5885 /**
5886  * @class Roo.data.ScriptTagProxy
5887  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5888  * other than the originating domain of the running page.<br><br>
5889  * <p>
5890  * <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
5891  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5892  * <p>
5893  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5894  * source code that is used as the source inside a &lt;script> tag.<br><br>
5895  * <p>
5896  * In order for the browser to process the returned data, the server must wrap the data object
5897  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5898  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5899  * depending on whether the callback name was passed:
5900  * <p>
5901  * <pre><code>
5902 boolean scriptTag = false;
5903 String cb = request.getParameter("callback");
5904 if (cb != null) {
5905     scriptTag = true;
5906     response.setContentType("text/javascript");
5907 } else {
5908     response.setContentType("application/x-json");
5909 }
5910 Writer out = response.getWriter();
5911 if (scriptTag) {
5912     out.write(cb + "(");
5913 }
5914 out.print(dataBlock.toJsonString());
5915 if (scriptTag) {
5916     out.write(");");
5917 }
5918 </pre></code>
5919  *
5920  * @constructor
5921  * @param {Object} config A configuration object.
5922  */
5923 Roo.data.ScriptTagProxy = function(config){
5924     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5925     Roo.apply(this, config);
5926     this.head = document.getElementsByTagName("head")[0];
5927 };
5928
5929 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5930
5931 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5932     /**
5933      * @cfg {String} url The URL from which to request the data object.
5934      */
5935     /**
5936      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5937      */
5938     timeout : 30000,
5939     /**
5940      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5941      * the server the name of the callback function set up by the load call to process the returned data object.
5942      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5943      * javascript output which calls this named function passing the data object as its only parameter.
5944      */
5945     callbackParam : "callback",
5946     /**
5947      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5948      * name to the request.
5949      */
5950     nocache : true,
5951
5952     /**
5953      * Load data from the configured URL, read the data object into
5954      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5955      * process that block using the passed callback.
5956      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5957      * for the request to the remote server.
5958      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5959      * object into a block of Roo.data.Records.
5960      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5961      * The function must be passed <ul>
5962      * <li>The Record block object</li>
5963      * <li>The "arg" argument from the load function</li>
5964      * <li>A boolean success indicator</li>
5965      * </ul>
5966      * @param {Object} scope The scope in which to call the callback
5967      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5968      */
5969     load : function(params, reader, callback, scope, arg){
5970         if(this.fireEvent("beforeload", this, params) !== false){
5971
5972             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5973
5974             var url = this.url;
5975             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5976             if(this.nocache){
5977                 url += "&_dc=" + (new Date().getTime());
5978             }
5979             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5980             var trans = {
5981                 id : transId,
5982                 cb : "stcCallback"+transId,
5983                 scriptId : "stcScript"+transId,
5984                 params : params,
5985                 arg : arg,
5986                 url : url,
5987                 callback : callback,
5988                 scope : scope,
5989                 reader : reader
5990             };
5991             var conn = this;
5992
5993             window[trans.cb] = function(o){
5994                 conn.handleResponse(o, trans);
5995             };
5996
5997             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5998
5999             if(this.autoAbort !== false){
6000                 this.abort();
6001             }
6002
6003             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6004
6005             var script = document.createElement("script");
6006             script.setAttribute("src", url);
6007             script.setAttribute("type", "text/javascript");
6008             script.setAttribute("id", trans.scriptId);
6009             this.head.appendChild(script);
6010
6011             this.trans = trans;
6012         }else{
6013             callback.call(scope||this, null, arg, false);
6014         }
6015     },
6016
6017     // private
6018     isLoading : function(){
6019         return this.trans ? true : false;
6020     },
6021
6022     /**
6023      * Abort the current server request.
6024      */
6025     abort : function(){
6026         if(this.isLoading()){
6027             this.destroyTrans(this.trans);
6028         }
6029     },
6030
6031     // private
6032     destroyTrans : function(trans, isLoaded){
6033         this.head.removeChild(document.getElementById(trans.scriptId));
6034         clearTimeout(trans.timeoutId);
6035         if(isLoaded){
6036             window[trans.cb] = undefined;
6037             try{
6038                 delete window[trans.cb];
6039             }catch(e){}
6040         }else{
6041             // if hasn't been loaded, wait for load to remove it to prevent script error
6042             window[trans.cb] = function(){
6043                 window[trans.cb] = undefined;
6044                 try{
6045                     delete window[trans.cb];
6046                 }catch(e){}
6047             };
6048         }
6049     },
6050
6051     // private
6052     handleResponse : function(o, trans){
6053         this.trans = false;
6054         this.destroyTrans(trans, true);
6055         var result;
6056         try {
6057             result = trans.reader.readRecords(o);
6058         }catch(e){
6059             this.fireEvent("loadexception", this, o, trans.arg, e);
6060             trans.callback.call(trans.scope||window, null, trans.arg, false);
6061             return;
6062         }
6063         this.fireEvent("load", this, o, trans.arg);
6064         trans.callback.call(trans.scope||window, result, trans.arg, true);
6065     },
6066
6067     // private
6068     handleFailure : function(trans){
6069         this.trans = false;
6070         this.destroyTrans(trans, false);
6071         this.fireEvent("loadexception", this, null, trans.arg);
6072         trans.callback.call(trans.scope||window, null, trans.arg, false);
6073     }
6074 });/*
6075  * Based on:
6076  * Ext JS Library 1.1.1
6077  * Copyright(c) 2006-2007, Ext JS, LLC.
6078  *
6079  * Originally Released Under LGPL - original licence link has changed is not relivant.
6080  *
6081  * Fork - LGPL
6082  * <script type="text/javascript">
6083  */
6084
6085 /**
6086  * @class Roo.data.JsonReader
6087  * @extends Roo.data.DataReader
6088  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6089  * based on mappings in a provided Roo.data.Record constructor.
6090  * 
6091  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6092  * in the reply previously. 
6093  * 
6094  * <p>
6095  * Example code:
6096  * <pre><code>
6097 var RecordDef = Roo.data.Record.create([
6098     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6099     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6100 ]);
6101 var myReader = new Roo.data.JsonReader({
6102     totalProperty: "results",    // The property which contains the total dataset size (optional)
6103     root: "rows",                // The property which contains an Array of row objects
6104     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6105 }, RecordDef);
6106 </code></pre>
6107  * <p>
6108  * This would consume a JSON file like this:
6109  * <pre><code>
6110 { 'results': 2, 'rows': [
6111     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6112     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6113 }
6114 </code></pre>
6115  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6116  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6117  * paged from the remote server.
6118  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6119  * @cfg {String} root name of the property which contains the Array of row objects.
6120  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6121  * @constructor
6122  * Create a new JsonReader
6123  * @param {Object} meta Metadata configuration options
6124  * @param {Object} recordType Either an Array of field definition objects,
6125  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6126  */
6127 Roo.data.JsonReader = function(meta, recordType){
6128     
6129     meta = meta || {};
6130     // set some defaults:
6131     Roo.applyIf(meta, {
6132         totalProperty: 'total',
6133         successProperty : 'success',
6134         root : 'data',
6135         id : 'id'
6136     });
6137     
6138     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6139 };
6140 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6141     
6142     /**
6143      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6144      * Used by Store query builder to append _requestMeta to params.
6145      * 
6146      */
6147     metaFromRemote : false,
6148     /**
6149      * This method is only used by a DataProxy which has retrieved data from a remote server.
6150      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6151      * @return {Object} data A data block which is used by an Roo.data.Store object as
6152      * a cache of Roo.data.Records.
6153      */
6154     read : function(response){
6155         var json = response.responseText;
6156        
6157         var o = /* eval:var:o */ eval("("+json+")");
6158         if(!o) {
6159             throw {message: "JsonReader.read: Json object not found"};
6160         }
6161         
6162         if(o.metaData){
6163             
6164             delete this.ef;
6165             this.metaFromRemote = true;
6166             this.meta = o.metaData;
6167             this.recordType = Roo.data.Record.create(o.metaData.fields);
6168             this.onMetaChange(this.meta, this.recordType, o);
6169         }
6170         return this.readRecords(o);
6171     },
6172
6173     // private function a store will implement
6174     onMetaChange : function(meta, recordType, o){
6175
6176     },
6177
6178     /**
6179          * @ignore
6180          */
6181     simpleAccess: function(obj, subsc) {
6182         return obj[subsc];
6183     },
6184
6185         /**
6186          * @ignore
6187          */
6188     getJsonAccessor: function(){
6189         var re = /[\[\.]/;
6190         return function(expr) {
6191             try {
6192                 return(re.test(expr))
6193                     ? new Function("obj", "return obj." + expr)
6194                     : function(obj){
6195                         return obj[expr];
6196                     };
6197             } catch(e){}
6198             return Roo.emptyFn;
6199         };
6200     }(),
6201
6202     /**
6203      * Create a data block containing Roo.data.Records from an XML document.
6204      * @param {Object} o An object which contains an Array of row objects in the property specified
6205      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6206      * which contains the total size of the dataset.
6207      * @return {Object} data A data block which is used by an Roo.data.Store object as
6208      * a cache of Roo.data.Records.
6209      */
6210     readRecords : function(o){
6211         /**
6212          * After any data loads, the raw JSON data is available for further custom processing.
6213          * @type Object
6214          */
6215         this.jsonData = o;
6216         var s = this.meta, Record = this.recordType,
6217             f = Record.prototype.fields, fi = f.items, fl = f.length;
6218
6219 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6220         if (!this.ef) {
6221             if(s.totalProperty) {
6222                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6223                 }
6224                 if(s.successProperty) {
6225                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6226                 }
6227                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6228                 if (s.id) {
6229                         var g = this.getJsonAccessor(s.id);
6230                         this.getId = function(rec) {
6231                                 var r = g(rec);
6232                                 return (r === undefined || r === "") ? null : r;
6233                         };
6234                 } else {
6235                         this.getId = function(){return null;};
6236                 }
6237             this.ef = [];
6238             for(var jj = 0; jj < fl; jj++){
6239                 f = fi[jj];
6240                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6241                 this.ef[jj] = this.getJsonAccessor(map);
6242             }
6243         }
6244
6245         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6246         if(s.totalProperty){
6247             var vt = parseInt(this.getTotal(o), 10);
6248             if(!isNaN(vt)){
6249                 totalRecords = vt;
6250             }
6251         }
6252         if(s.successProperty){
6253             var vs = this.getSuccess(o);
6254             if(vs === false || vs === 'false'){
6255                 success = false;
6256             }
6257         }
6258         var records = [];
6259             for(var i = 0; i < c; i++){
6260                     var n = root[i];
6261                 var values = {};
6262                 var id = this.getId(n);
6263                 for(var j = 0; j < fl; j++){
6264                     f = fi[j];
6265                 var v = this.ef[j](n);
6266                 if (!f.convert) {
6267                     Roo.log('missing convert for ' + f.name);
6268                     Roo.log(f);
6269                     continue;
6270                 }
6271                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6272                 }
6273                 var record = new Record(values, id);
6274                 record.json = n;
6275                 records[i] = record;
6276             }
6277             return {
6278                 success : success,
6279                 records : records,
6280                 totalRecords : totalRecords
6281             };
6282     }
6283 });/*
6284  * Based on:
6285  * Ext JS Library 1.1.1
6286  * Copyright(c) 2006-2007, Ext JS, LLC.
6287  *
6288  * Originally Released Under LGPL - original licence link has changed is not relivant.
6289  *
6290  * Fork - LGPL
6291  * <script type="text/javascript">
6292  */
6293
6294 /**
6295  * @class Roo.data.XmlReader
6296  * @extends Roo.data.DataReader
6297  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6298  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6299  * <p>
6300  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6301  * header in the HTTP response must be set to "text/xml".</em>
6302  * <p>
6303  * Example code:
6304  * <pre><code>
6305 var RecordDef = Roo.data.Record.create([
6306    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6307    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6308 ]);
6309 var myReader = new Roo.data.XmlReader({
6310    totalRecords: "results", // The element which contains the total dataset size (optional)
6311    record: "row",           // The repeated element which contains row information
6312    id: "id"                 // The element within the row that provides an ID for the record (optional)
6313 }, RecordDef);
6314 </code></pre>
6315  * <p>
6316  * This would consume an XML file like this:
6317  * <pre><code>
6318 &lt;?xml?>
6319 &lt;dataset>
6320  &lt;results>2&lt;/results>
6321  &lt;row>
6322    &lt;id>1&lt;/id>
6323    &lt;name>Bill&lt;/name>
6324    &lt;occupation>Gardener&lt;/occupation>
6325  &lt;/row>
6326  &lt;row>
6327    &lt;id>2&lt;/id>
6328    &lt;name>Ben&lt;/name>
6329    &lt;occupation>Horticulturalist&lt;/occupation>
6330  &lt;/row>
6331 &lt;/dataset>
6332 </code></pre>
6333  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6334  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6335  * paged from the remote server.
6336  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6337  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6338  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6339  * a record identifier value.
6340  * @constructor
6341  * Create a new XmlReader
6342  * @param {Object} meta Metadata configuration options
6343  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6344  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6345  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6346  */
6347 Roo.data.XmlReader = function(meta, recordType){
6348     meta = meta || {};
6349     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6350 };
6351 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6352     /**
6353      * This method is only used by a DataProxy which has retrieved data from a remote server.
6354          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6355          * to contain a method called 'responseXML' that returns an XML document object.
6356      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6357      * a cache of Roo.data.Records.
6358      */
6359     read : function(response){
6360         var doc = response.responseXML;
6361         if(!doc) {
6362             throw {message: "XmlReader.read: XML Document not available"};
6363         }
6364         return this.readRecords(doc);
6365     },
6366
6367     /**
6368      * Create a data block containing Roo.data.Records from an XML document.
6369          * @param {Object} doc A parsed XML document.
6370      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6371      * a cache of Roo.data.Records.
6372      */
6373     readRecords : function(doc){
6374         /**
6375          * After any data loads/reads, the raw XML Document is available for further custom processing.
6376          * @type XMLDocument
6377          */
6378         this.xmlData = doc;
6379         var root = doc.documentElement || doc;
6380         var q = Roo.DomQuery;
6381         var recordType = this.recordType, fields = recordType.prototype.fields;
6382         var sid = this.meta.id;
6383         var totalRecords = 0, success = true;
6384         if(this.meta.totalRecords){
6385             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6386         }
6387         
6388         if(this.meta.success){
6389             var sv = q.selectValue(this.meta.success, root, true);
6390             success = sv !== false && sv !== 'false';
6391         }
6392         var records = [];
6393         var ns = q.select(this.meta.record, root);
6394         for(var i = 0, len = ns.length; i < len; i++) {
6395                 var n = ns[i];
6396                 var values = {};
6397                 var id = sid ? q.selectValue(sid, n) : undefined;
6398                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6399                     var f = fields.items[j];
6400                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6401                     v = f.convert(v);
6402                     values[f.name] = v;
6403                 }
6404                 var record = new recordType(values, id);
6405                 record.node = n;
6406                 records[records.length] = record;
6407             }
6408
6409             return {
6410                 success : success,
6411                 records : records,
6412                 totalRecords : totalRecords || records.length
6413             };
6414     }
6415 });/*
6416  * Based on:
6417  * Ext JS Library 1.1.1
6418  * Copyright(c) 2006-2007, Ext JS, LLC.
6419  *
6420  * Originally Released Under LGPL - original licence link has changed is not relivant.
6421  *
6422  * Fork - LGPL
6423  * <script type="text/javascript">
6424  */
6425
6426 /**
6427  * @class Roo.data.ArrayReader
6428  * @extends Roo.data.DataReader
6429  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6430  * Each element of that Array represents a row of data fields. The
6431  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6432  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6433  * <p>
6434  * Example code:.
6435  * <pre><code>
6436 var RecordDef = Roo.data.Record.create([
6437     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6438     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6439 ]);
6440 var myReader = new Roo.data.ArrayReader({
6441     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6442 }, RecordDef);
6443 </code></pre>
6444  * <p>
6445  * This would consume an Array like this:
6446  * <pre><code>
6447 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6448   </code></pre>
6449  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6450  * @constructor
6451  * Create a new JsonReader
6452  * @param {Object} meta Metadata configuration options.
6453  * @param {Object} recordType Either an Array of field definition objects
6454  * as specified to {@link Roo.data.Record#create},
6455  * or an {@link Roo.data.Record} object
6456  * created using {@link Roo.data.Record#create}.
6457  */
6458 Roo.data.ArrayReader = function(meta, recordType){
6459     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6460 };
6461
6462 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6463     /**
6464      * Create a data block containing Roo.data.Records from an XML document.
6465      * @param {Object} o An Array of row objects which represents the dataset.
6466      * @return {Object} data A data block which is used by an Roo.data.Store object as
6467      * a cache of Roo.data.Records.
6468      */
6469     readRecords : function(o){
6470         var sid = this.meta ? this.meta.id : null;
6471         var recordType = this.recordType, fields = recordType.prototype.fields;
6472         var records = [];
6473         var root = o;
6474             for(var i = 0; i < root.length; i++){
6475                     var n = root[i];
6476                 var values = {};
6477                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6478                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6479                 var f = fields.items[j];
6480                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6481                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6482                 v = f.convert(v);
6483                 values[f.name] = v;
6484             }
6485                 var record = new recordType(values, id);
6486                 record.json = n;
6487                 records[records.length] = record;
6488             }
6489             return {
6490                 records : records,
6491                 totalRecords : records.length
6492             };
6493     }
6494 });/*
6495  * Based on:
6496  * Ext JS Library 1.1.1
6497  * Copyright(c) 2006-2007, Ext JS, LLC.
6498  *
6499  * Originally Released Under LGPL - original licence link has changed is not relivant.
6500  *
6501  * Fork - LGPL
6502  * <script type="text/javascript">
6503  */
6504
6505
6506 /**
6507  * @class Roo.data.Tree
6508  * @extends Roo.util.Observable
6509  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6510  * in the tree have most standard DOM functionality.
6511  * @constructor
6512  * @param {Node} root (optional) The root node
6513  */
6514 Roo.data.Tree = function(root){
6515    this.nodeHash = {};
6516    /**
6517     * The root node for this tree
6518     * @type Node
6519     */
6520    this.root = null;
6521    if(root){
6522        this.setRootNode(root);
6523    }
6524    this.addEvents({
6525        /**
6526         * @event append
6527         * Fires when a new child node is appended to a node in this tree.
6528         * @param {Tree} tree The owner tree
6529         * @param {Node} parent The parent node
6530         * @param {Node} node The newly appended node
6531         * @param {Number} index The index of the newly appended node
6532         */
6533        "append" : true,
6534        /**
6535         * @event remove
6536         * Fires when a child node is removed from a node in this tree.
6537         * @param {Tree} tree The owner tree
6538         * @param {Node} parent The parent node
6539         * @param {Node} node The child node removed
6540         */
6541        "remove" : true,
6542        /**
6543         * @event move
6544         * Fires when a node is moved to a new location in the tree
6545         * @param {Tree} tree The owner tree
6546         * @param {Node} node The node moved
6547         * @param {Node} oldParent The old parent of this node
6548         * @param {Node} newParent The new parent of this node
6549         * @param {Number} index The index it was moved to
6550         */
6551        "move" : true,
6552        /**
6553         * @event insert
6554         * Fires when a new child node is inserted in a node in this tree.
6555         * @param {Tree} tree The owner tree
6556         * @param {Node} parent The parent node
6557         * @param {Node} node The child node inserted
6558         * @param {Node} refNode The child node the node was inserted before
6559         */
6560        "insert" : true,
6561        /**
6562         * @event beforeappend
6563         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6564         * @param {Tree} tree The owner tree
6565         * @param {Node} parent The parent node
6566         * @param {Node} node The child node to be appended
6567         */
6568        "beforeappend" : true,
6569        /**
6570         * @event beforeremove
6571         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6572         * @param {Tree} tree The owner tree
6573         * @param {Node} parent The parent node
6574         * @param {Node} node The child node to be removed
6575         */
6576        "beforeremove" : true,
6577        /**
6578         * @event beforemove
6579         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6580         * @param {Tree} tree The owner tree
6581         * @param {Node} node The node being moved
6582         * @param {Node} oldParent The parent of the node
6583         * @param {Node} newParent The new parent the node is moving to
6584         * @param {Number} index The index it is being moved to
6585         */
6586        "beforemove" : true,
6587        /**
6588         * @event beforeinsert
6589         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6590         * @param {Tree} tree The owner tree
6591         * @param {Node} parent The parent node
6592         * @param {Node} node The child node to be inserted
6593         * @param {Node} refNode The child node the node is being inserted before
6594         */
6595        "beforeinsert" : true
6596    });
6597
6598     Roo.data.Tree.superclass.constructor.call(this);
6599 };
6600
6601 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6602     pathSeparator: "/",
6603
6604     proxyNodeEvent : function(){
6605         return this.fireEvent.apply(this, arguments);
6606     },
6607
6608     /**
6609      * Returns the root node for this tree.
6610      * @return {Node}
6611      */
6612     getRootNode : function(){
6613         return this.root;
6614     },
6615
6616     /**
6617      * Sets the root node for this tree.
6618      * @param {Node} node
6619      * @return {Node}
6620      */
6621     setRootNode : function(node){
6622         this.root = node;
6623         node.ownerTree = this;
6624         node.isRoot = true;
6625         this.registerNode(node);
6626         return node;
6627     },
6628
6629     /**
6630      * Gets a node in this tree by its id.
6631      * @param {String} id
6632      * @return {Node}
6633      */
6634     getNodeById : function(id){
6635         return this.nodeHash[id];
6636     },
6637
6638     registerNode : function(node){
6639         this.nodeHash[node.id] = node;
6640     },
6641
6642     unregisterNode : function(node){
6643         delete this.nodeHash[node.id];
6644     },
6645
6646     toString : function(){
6647         return "[Tree"+(this.id?" "+this.id:"")+"]";
6648     }
6649 });
6650
6651 /**
6652  * @class Roo.data.Node
6653  * @extends Roo.util.Observable
6654  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6655  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6656  * @constructor
6657  * @param {Object} attributes The attributes/config for the node
6658  */
6659 Roo.data.Node = function(attributes){
6660     /**
6661      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6662      * @type {Object}
6663      */
6664     this.attributes = attributes || {};
6665     this.leaf = this.attributes.leaf;
6666     /**
6667      * The node id. @type String
6668      */
6669     this.id = this.attributes.id;
6670     if(!this.id){
6671         this.id = Roo.id(null, "ynode-");
6672         this.attributes.id = this.id;
6673     }
6674      
6675     
6676     /**
6677      * All child nodes of this node. @type Array
6678      */
6679     this.childNodes = [];
6680     if(!this.childNodes.indexOf){ // indexOf is a must
6681         this.childNodes.indexOf = function(o){
6682             for(var i = 0, len = this.length; i < len; i++){
6683                 if(this[i] == o) {
6684                     return i;
6685                 }
6686             }
6687             return -1;
6688         };
6689     }
6690     /**
6691      * The parent node for this node. @type Node
6692      */
6693     this.parentNode = null;
6694     /**
6695      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6696      */
6697     this.firstChild = null;
6698     /**
6699      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6700      */
6701     this.lastChild = null;
6702     /**
6703      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6704      */
6705     this.previousSibling = null;
6706     /**
6707      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6708      */
6709     this.nextSibling = null;
6710
6711     this.addEvents({
6712        /**
6713         * @event append
6714         * Fires when a new child node is appended
6715         * @param {Tree} tree The owner tree
6716         * @param {Node} this This node
6717         * @param {Node} node The newly appended node
6718         * @param {Number} index The index of the newly appended node
6719         */
6720        "append" : true,
6721        /**
6722         * @event remove
6723         * Fires when a child node is removed
6724         * @param {Tree} tree The owner tree
6725         * @param {Node} this This node
6726         * @param {Node} node The removed node
6727         */
6728        "remove" : true,
6729        /**
6730         * @event move
6731         * Fires when this node is moved to a new location in the tree
6732         * @param {Tree} tree The owner tree
6733         * @param {Node} this This node
6734         * @param {Node} oldParent The old parent of this node
6735         * @param {Node} newParent The new parent of this node
6736         * @param {Number} index The index it was moved to
6737         */
6738        "move" : true,
6739        /**
6740         * @event insert
6741         * Fires when a new child node is inserted.
6742         * @param {Tree} tree The owner tree
6743         * @param {Node} this This node
6744         * @param {Node} node The child node inserted
6745         * @param {Node} refNode The child node the node was inserted before
6746         */
6747        "insert" : true,
6748        /**
6749         * @event beforeappend
6750         * Fires before a new child is appended, return false to cancel the append.
6751         * @param {Tree} tree The owner tree
6752         * @param {Node} this This node
6753         * @param {Node} node The child node to be appended
6754         */
6755        "beforeappend" : true,
6756        /**
6757         * @event beforeremove
6758         * Fires before a child is removed, return false to cancel the remove.
6759         * @param {Tree} tree The owner tree
6760         * @param {Node} this This node
6761         * @param {Node} node The child node to be removed
6762         */
6763        "beforeremove" : true,
6764        /**
6765         * @event beforemove
6766         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6767         * @param {Tree} tree The owner tree
6768         * @param {Node} this This node
6769         * @param {Node} oldParent The parent of this node
6770         * @param {Node} newParent The new parent this node is moving to
6771         * @param {Number} index The index it is being moved to
6772         */
6773        "beforemove" : true,
6774        /**
6775         * @event beforeinsert
6776         * Fires before a new child is inserted, return false to cancel the insert.
6777         * @param {Tree} tree The owner tree
6778         * @param {Node} this This node
6779         * @param {Node} node The child node to be inserted
6780         * @param {Node} refNode The child node the node is being inserted before
6781         */
6782        "beforeinsert" : true
6783    });
6784     this.listeners = this.attributes.listeners;
6785     Roo.data.Node.superclass.constructor.call(this);
6786 };
6787
6788 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6789     fireEvent : function(evtName){
6790         // first do standard event for this node
6791         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6792             return false;
6793         }
6794         // then bubble it up to the tree if the event wasn't cancelled
6795         var ot = this.getOwnerTree();
6796         if(ot){
6797             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6798                 return false;
6799             }
6800         }
6801         return true;
6802     },
6803
6804     /**
6805      * Returns true if this node is a leaf
6806      * @return {Boolean}
6807      */
6808     isLeaf : function(){
6809         return this.leaf === true;
6810     },
6811
6812     // private
6813     setFirstChild : function(node){
6814         this.firstChild = node;
6815     },
6816
6817     //private
6818     setLastChild : function(node){
6819         this.lastChild = node;
6820     },
6821
6822
6823     /**
6824      * Returns true if this node is the last child of its parent
6825      * @return {Boolean}
6826      */
6827     isLast : function(){
6828        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6829     },
6830
6831     /**
6832      * Returns true if this node is the first child of its parent
6833      * @return {Boolean}
6834      */
6835     isFirst : function(){
6836        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6837     },
6838
6839     hasChildNodes : function(){
6840         return !this.isLeaf() && this.childNodes.length > 0;
6841     },
6842
6843     /**
6844      * Insert node(s) as the last child node of this node.
6845      * @param {Node/Array} node The node or Array of nodes to append
6846      * @return {Node} The appended node if single append, or null if an array was passed
6847      */
6848     appendChild : function(node){
6849         var multi = false;
6850         if(node instanceof Array){
6851             multi = node;
6852         }else if(arguments.length > 1){
6853             multi = arguments;
6854         }
6855         // if passed an array or multiple args do them one by one
6856         if(multi){
6857             for(var i = 0, len = multi.length; i < len; i++) {
6858                 this.appendChild(multi[i]);
6859             }
6860         }else{
6861             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6862                 return false;
6863             }
6864             var index = this.childNodes.length;
6865             var oldParent = node.parentNode;
6866             // it's a move, make sure we move it cleanly
6867             if(oldParent){
6868                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6869                     return false;
6870                 }
6871                 oldParent.removeChild(node);
6872             }
6873             index = this.childNodes.length;
6874             if(index == 0){
6875                 this.setFirstChild(node);
6876             }
6877             this.childNodes.push(node);
6878             node.parentNode = this;
6879             var ps = this.childNodes[index-1];
6880             if(ps){
6881                 node.previousSibling = ps;
6882                 ps.nextSibling = node;
6883             }else{
6884                 node.previousSibling = null;
6885             }
6886             node.nextSibling = null;
6887             this.setLastChild(node);
6888             node.setOwnerTree(this.getOwnerTree());
6889             this.fireEvent("append", this.ownerTree, this, node, index);
6890             if(oldParent){
6891                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6892             }
6893             return node;
6894         }
6895     },
6896
6897     /**
6898      * Removes a child node from this node.
6899      * @param {Node} node The node to remove
6900      * @return {Node} The removed node
6901      */
6902     removeChild : function(node){
6903         var index = this.childNodes.indexOf(node);
6904         if(index == -1){
6905             return false;
6906         }
6907         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6908             return false;
6909         }
6910
6911         // remove it from childNodes collection
6912         this.childNodes.splice(index, 1);
6913
6914         // update siblings
6915         if(node.previousSibling){
6916             node.previousSibling.nextSibling = node.nextSibling;
6917         }
6918         if(node.nextSibling){
6919             node.nextSibling.previousSibling = node.previousSibling;
6920         }
6921
6922         // update child refs
6923         if(this.firstChild == node){
6924             this.setFirstChild(node.nextSibling);
6925         }
6926         if(this.lastChild == node){
6927             this.setLastChild(node.previousSibling);
6928         }
6929
6930         node.setOwnerTree(null);
6931         // clear any references from the node
6932         node.parentNode = null;
6933         node.previousSibling = null;
6934         node.nextSibling = null;
6935         this.fireEvent("remove", this.ownerTree, this, node);
6936         return node;
6937     },
6938
6939     /**
6940      * Inserts the first node before the second node in this nodes childNodes collection.
6941      * @param {Node} node The node to insert
6942      * @param {Node} refNode The node to insert before (if null the node is appended)
6943      * @return {Node} The inserted node
6944      */
6945     insertBefore : function(node, refNode){
6946         if(!refNode){ // like standard Dom, refNode can be null for append
6947             return this.appendChild(node);
6948         }
6949         // nothing to do
6950         if(node == refNode){
6951             return false;
6952         }
6953
6954         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6955             return false;
6956         }
6957         var index = this.childNodes.indexOf(refNode);
6958         var oldParent = node.parentNode;
6959         var refIndex = index;
6960
6961         // when moving internally, indexes will change after remove
6962         if(oldParent == this && this.childNodes.indexOf(node) < index){
6963             refIndex--;
6964         }
6965
6966         // it's a move, make sure we move it cleanly
6967         if(oldParent){
6968             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6969                 return false;
6970             }
6971             oldParent.removeChild(node);
6972         }
6973         if(refIndex == 0){
6974             this.setFirstChild(node);
6975         }
6976         this.childNodes.splice(refIndex, 0, node);
6977         node.parentNode = this;
6978         var ps = this.childNodes[refIndex-1];
6979         if(ps){
6980             node.previousSibling = ps;
6981             ps.nextSibling = node;
6982         }else{
6983             node.previousSibling = null;
6984         }
6985         node.nextSibling = refNode;
6986         refNode.previousSibling = node;
6987         node.setOwnerTree(this.getOwnerTree());
6988         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6989         if(oldParent){
6990             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6991         }
6992         return node;
6993     },
6994
6995     /**
6996      * Returns the child node at the specified index.
6997      * @param {Number} index
6998      * @return {Node}
6999      */
7000     item : function(index){
7001         return this.childNodes[index];
7002     },
7003
7004     /**
7005      * Replaces one child node in this node with another.
7006      * @param {Node} newChild The replacement node
7007      * @param {Node} oldChild The node to replace
7008      * @return {Node} The replaced node
7009      */
7010     replaceChild : function(newChild, oldChild){
7011         this.insertBefore(newChild, oldChild);
7012         this.removeChild(oldChild);
7013         return oldChild;
7014     },
7015
7016     /**
7017      * Returns the index of a child node
7018      * @param {Node} node
7019      * @return {Number} The index of the node or -1 if it was not found
7020      */
7021     indexOf : function(child){
7022         return this.childNodes.indexOf(child);
7023     },
7024
7025     /**
7026      * Returns the tree this node is in.
7027      * @return {Tree}
7028      */
7029     getOwnerTree : function(){
7030         // if it doesn't have one, look for one
7031         if(!this.ownerTree){
7032             var p = this;
7033             while(p){
7034                 if(p.ownerTree){
7035                     this.ownerTree = p.ownerTree;
7036                     break;
7037                 }
7038                 p = p.parentNode;
7039             }
7040         }
7041         return this.ownerTree;
7042     },
7043
7044     /**
7045      * Returns depth of this node (the root node has a depth of 0)
7046      * @return {Number}
7047      */
7048     getDepth : function(){
7049         var depth = 0;
7050         var p = this;
7051         while(p.parentNode){
7052             ++depth;
7053             p = p.parentNode;
7054         }
7055         return depth;
7056     },
7057
7058     // private
7059     setOwnerTree : function(tree){
7060         // if it's move, we need to update everyone
7061         if(tree != this.ownerTree){
7062             if(this.ownerTree){
7063                 this.ownerTree.unregisterNode(this);
7064             }
7065             this.ownerTree = tree;
7066             var cs = this.childNodes;
7067             for(var i = 0, len = cs.length; i < len; i++) {
7068                 cs[i].setOwnerTree(tree);
7069             }
7070             if(tree){
7071                 tree.registerNode(this);
7072             }
7073         }
7074     },
7075
7076     /**
7077      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7078      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7079      * @return {String} The path
7080      */
7081     getPath : function(attr){
7082         attr = attr || "id";
7083         var p = this.parentNode;
7084         var b = [this.attributes[attr]];
7085         while(p){
7086             b.unshift(p.attributes[attr]);
7087             p = p.parentNode;
7088         }
7089         var sep = this.getOwnerTree().pathSeparator;
7090         return sep + b.join(sep);
7091     },
7092
7093     /**
7094      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7095      * function call will be the scope provided or the current node. The arguments to the function
7096      * will be the args provided or the current node. If the function returns false at any point,
7097      * the bubble is stopped.
7098      * @param {Function} fn The function to call
7099      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7100      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7101      */
7102     bubble : function(fn, scope, args){
7103         var p = this;
7104         while(p){
7105             if(fn.call(scope || p, args || p) === false){
7106                 break;
7107             }
7108             p = p.parentNode;
7109         }
7110     },
7111
7112     /**
7113      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7114      * function call will be the scope provided or the current node. The arguments to the function
7115      * will be the args provided or the current node. If the function returns false at any point,
7116      * the cascade is stopped on that branch.
7117      * @param {Function} fn The function to call
7118      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7119      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7120      */
7121     cascade : function(fn, scope, args){
7122         if(fn.call(scope || this, args || this) !== false){
7123             var cs = this.childNodes;
7124             for(var i = 0, len = cs.length; i < len; i++) {
7125                 cs[i].cascade(fn, scope, args);
7126             }
7127         }
7128     },
7129
7130     /**
7131      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7132      * function call will be the scope provided or the current node. The arguments to the function
7133      * will be the args provided or the current node. If the function returns false at any point,
7134      * the iteration stops.
7135      * @param {Function} fn The function to call
7136      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7137      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7138      */
7139     eachChild : function(fn, scope, args){
7140         var cs = this.childNodes;
7141         for(var i = 0, len = cs.length; i < len; i++) {
7142                 if(fn.call(scope || this, args || cs[i]) === false){
7143                     break;
7144                 }
7145         }
7146     },
7147
7148     /**
7149      * Finds the first child that has the attribute with the specified value.
7150      * @param {String} attribute The attribute name
7151      * @param {Mixed} value The value to search for
7152      * @return {Node} The found child or null if none was found
7153      */
7154     findChild : function(attribute, value){
7155         var cs = this.childNodes;
7156         for(var i = 0, len = cs.length; i < len; i++) {
7157                 if(cs[i].attributes[attribute] == value){
7158                     return cs[i];
7159                 }
7160         }
7161         return null;
7162     },
7163
7164     /**
7165      * Finds the first child by a custom function. The child matches if the function passed
7166      * returns true.
7167      * @param {Function} fn
7168      * @param {Object} scope (optional)
7169      * @return {Node} The found child or null if none was found
7170      */
7171     findChildBy : function(fn, scope){
7172         var cs = this.childNodes;
7173         for(var i = 0, len = cs.length; i < len; i++) {
7174                 if(fn.call(scope||cs[i], cs[i]) === true){
7175                     return cs[i];
7176                 }
7177         }
7178         return null;
7179     },
7180
7181     /**
7182      * Sorts this nodes children using the supplied sort function
7183      * @param {Function} fn
7184      * @param {Object} scope (optional)
7185      */
7186     sort : function(fn, scope){
7187         var cs = this.childNodes;
7188         var len = cs.length;
7189         if(len > 0){
7190             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7191             cs.sort(sortFn);
7192             for(var i = 0; i < len; i++){
7193                 var n = cs[i];
7194                 n.previousSibling = cs[i-1];
7195                 n.nextSibling = cs[i+1];
7196                 if(i == 0){
7197                     this.setFirstChild(n);
7198                 }
7199                 if(i == len-1){
7200                     this.setLastChild(n);
7201                 }
7202             }
7203         }
7204     },
7205
7206     /**
7207      * Returns true if this node is an ancestor (at any point) of the passed node.
7208      * @param {Node} node
7209      * @return {Boolean}
7210      */
7211     contains : function(node){
7212         return node.isAncestor(this);
7213     },
7214
7215     /**
7216      * Returns true if the passed node is an ancestor (at any point) of this node.
7217      * @param {Node} node
7218      * @return {Boolean}
7219      */
7220     isAncestor : function(node){
7221         var p = this.parentNode;
7222         while(p){
7223             if(p == node){
7224                 return true;
7225             }
7226             p = p.parentNode;
7227         }
7228         return false;
7229     },
7230
7231     toString : function(){
7232         return "[Node"+(this.id?" "+this.id:"")+"]";
7233     }
7234 });/*
7235  * Based on:
7236  * Ext JS Library 1.1.1
7237  * Copyright(c) 2006-2007, Ext JS, LLC.
7238  *
7239  * Originally Released Under LGPL - original licence link has changed is not relivant.
7240  *
7241  * Fork - LGPL
7242  * <script type="text/javascript">
7243  */
7244  
7245
7246 /**
7247  * @class Roo.ComponentMgr
7248  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7249  * @singleton
7250  */
7251 Roo.ComponentMgr = function(){
7252     var all = new Roo.util.MixedCollection();
7253
7254     return {
7255         /**
7256          * Registers a component.
7257          * @param {Roo.Component} c The component
7258          */
7259         register : function(c){
7260             all.add(c);
7261         },
7262
7263         /**
7264          * Unregisters a component.
7265          * @param {Roo.Component} c The component
7266          */
7267         unregister : function(c){
7268             all.remove(c);
7269         },
7270
7271         /**
7272          * Returns a component by id
7273          * @param {String} id The component id
7274          */
7275         get : function(id){
7276             return all.get(id);
7277         },
7278
7279         /**
7280          * Registers a function that will be called when a specified component is added to ComponentMgr
7281          * @param {String} id The component id
7282          * @param {Funtction} fn The callback function
7283          * @param {Object} scope The scope of the callback
7284          */
7285         onAvailable : function(id, fn, scope){
7286             all.on("add", function(index, o){
7287                 if(o.id == id){
7288                     fn.call(scope || o, o);
7289                     all.un("add", fn, scope);
7290                 }
7291             });
7292         }
7293     };
7294 }();/*
7295  * Based on:
7296  * Ext JS Library 1.1.1
7297  * Copyright(c) 2006-2007, Ext JS, LLC.
7298  *
7299  * Originally Released Under LGPL - original licence link has changed is not relivant.
7300  *
7301  * Fork - LGPL
7302  * <script type="text/javascript">
7303  */
7304  
7305 /**
7306  * @class Roo.Component
7307  * @extends Roo.util.Observable
7308  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7309  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7310  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7311  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7312  * All visual components (widgets) that require rendering into a layout should subclass Component.
7313  * @constructor
7314  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7315  * 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
7316  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7317  */
7318 Roo.Component = function(config){
7319     config = config || {};
7320     if(config.tagName || config.dom || typeof config == "string"){ // element object
7321         config = {el: config, id: config.id || config};
7322     }
7323     this.initialConfig = config;
7324
7325     Roo.apply(this, config);
7326     this.addEvents({
7327         /**
7328          * @event disable
7329          * Fires after the component is disabled.
7330              * @param {Roo.Component} this
7331              */
7332         disable : true,
7333         /**
7334          * @event enable
7335          * Fires after the component is enabled.
7336              * @param {Roo.Component} this
7337              */
7338         enable : true,
7339         /**
7340          * @event beforeshow
7341          * Fires before the component is shown.  Return false to stop the show.
7342              * @param {Roo.Component} this
7343              */
7344         beforeshow : true,
7345         /**
7346          * @event show
7347          * Fires after the component is shown.
7348              * @param {Roo.Component} this
7349              */
7350         show : true,
7351         /**
7352          * @event beforehide
7353          * Fires before the component is hidden. Return false to stop the hide.
7354              * @param {Roo.Component} this
7355              */
7356         beforehide : true,
7357         /**
7358          * @event hide
7359          * Fires after the component is hidden.
7360              * @param {Roo.Component} this
7361              */
7362         hide : true,
7363         /**
7364          * @event beforerender
7365          * Fires before the component is rendered. Return false to stop the render.
7366              * @param {Roo.Component} this
7367              */
7368         beforerender : true,
7369         /**
7370          * @event render
7371          * Fires after the component is rendered.
7372              * @param {Roo.Component} this
7373              */
7374         render : true,
7375         /**
7376          * @event beforedestroy
7377          * Fires before the component is destroyed. Return false to stop the destroy.
7378              * @param {Roo.Component} this
7379              */
7380         beforedestroy : true,
7381         /**
7382          * @event destroy
7383          * Fires after the component is destroyed.
7384              * @param {Roo.Component} this
7385              */
7386         destroy : true
7387     });
7388     if(!this.id){
7389         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7390     }
7391     Roo.ComponentMgr.register(this);
7392     Roo.Component.superclass.constructor.call(this);
7393     this.initComponent();
7394     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7395         this.render(this.renderTo);
7396         delete this.renderTo;
7397     }
7398 };
7399
7400 /** @private */
7401 Roo.Component.AUTO_ID = 1000;
7402
7403 Roo.extend(Roo.Component, Roo.util.Observable, {
7404     /**
7405      * @scope Roo.Component.prototype
7406      * @type {Boolean}
7407      * true if this component is hidden. Read-only.
7408      */
7409     hidden : false,
7410     /**
7411      * @type {Boolean}
7412      * true if this component is disabled. Read-only.
7413      */
7414     disabled : false,
7415     /**
7416      * @type {Boolean}
7417      * true if this component has been rendered. Read-only.
7418      */
7419     rendered : false,
7420     
7421     /** @cfg {String} disableClass
7422      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7423      */
7424     disabledClass : "x-item-disabled",
7425         /** @cfg {Boolean} allowDomMove
7426          * Whether the component can move the Dom node when rendering (defaults to true).
7427          */
7428     allowDomMove : true,
7429     /** @cfg {String} hideMode
7430      * How this component should hidden. Supported values are
7431      * "visibility" (css visibility), "offsets" (negative offset position) and
7432      * "display" (css display) - defaults to "display".
7433      */
7434     hideMode: 'display',
7435
7436     /** @private */
7437     ctype : "Roo.Component",
7438
7439     /**
7440      * @cfg {String} actionMode 
7441      * which property holds the element that used for  hide() / show() / disable() / enable()
7442      * default is 'el' 
7443      */
7444     actionMode : "el",
7445
7446     /** @private */
7447     getActionEl : function(){
7448         return this[this.actionMode];
7449     },
7450
7451     initComponent : Roo.emptyFn,
7452     /**
7453      * If this is a lazy rendering component, render it to its container element.
7454      * @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.
7455      */
7456     render : function(container, position){
7457         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7458             if(!container && this.el){
7459                 this.el = Roo.get(this.el);
7460                 container = this.el.dom.parentNode;
7461                 this.allowDomMove = false;
7462             }
7463             this.container = Roo.get(container);
7464             this.rendered = true;
7465             if(position !== undefined){
7466                 if(typeof position == 'number'){
7467                     position = this.container.dom.childNodes[position];
7468                 }else{
7469                     position = Roo.getDom(position);
7470                 }
7471             }
7472             this.onRender(this.container, position || null);
7473             if(this.cls){
7474                 this.el.addClass(this.cls);
7475                 delete this.cls;
7476             }
7477             if(this.style){
7478                 this.el.applyStyles(this.style);
7479                 delete this.style;
7480             }
7481             this.fireEvent("render", this);
7482             this.afterRender(this.container);
7483             if(this.hidden){
7484                 this.hide();
7485             }
7486             if(this.disabled){
7487                 this.disable();
7488             }
7489         }
7490         return this;
7491     },
7492
7493     /** @private */
7494     // default function is not really useful
7495     onRender : function(ct, position){
7496         if(this.el){
7497             this.el = Roo.get(this.el);
7498             if(this.allowDomMove !== false){
7499                 ct.dom.insertBefore(this.el.dom, position);
7500             }
7501         }
7502     },
7503
7504     /** @private */
7505     getAutoCreate : function(){
7506         var cfg = typeof this.autoCreate == "object" ?
7507                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7508         if(this.id && !cfg.id){
7509             cfg.id = this.id;
7510         }
7511         return cfg;
7512     },
7513
7514     /** @private */
7515     afterRender : Roo.emptyFn,
7516
7517     /**
7518      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7519      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7520      */
7521     destroy : function(){
7522         if(this.fireEvent("beforedestroy", this) !== false){
7523             this.purgeListeners();
7524             this.beforeDestroy();
7525             if(this.rendered){
7526                 this.el.removeAllListeners();
7527                 this.el.remove();
7528                 if(this.actionMode == "container"){
7529                     this.container.remove();
7530                 }
7531             }
7532             this.onDestroy();
7533             Roo.ComponentMgr.unregister(this);
7534             this.fireEvent("destroy", this);
7535         }
7536     },
7537
7538         /** @private */
7539     beforeDestroy : function(){
7540
7541     },
7542
7543         /** @private */
7544         onDestroy : function(){
7545
7546     },
7547
7548     /**
7549      * Returns the underlying {@link Roo.Element}.
7550      * @return {Roo.Element} The element
7551      */
7552     getEl : function(){
7553         return this.el;
7554     },
7555
7556     /**
7557      * Returns the id of this component.
7558      * @return {String}
7559      */
7560     getId : function(){
7561         return this.id;
7562     },
7563
7564     /**
7565      * Try to focus this component.
7566      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7567      * @return {Roo.Component} this
7568      */
7569     focus : function(selectText){
7570         if(this.rendered){
7571             this.el.focus();
7572             if(selectText === true){
7573                 this.el.dom.select();
7574             }
7575         }
7576         return this;
7577     },
7578
7579     /** @private */
7580     blur : function(){
7581         if(this.rendered){
7582             this.el.blur();
7583         }
7584         return this;
7585     },
7586
7587     /**
7588      * Disable this component.
7589      * @return {Roo.Component} this
7590      */
7591     disable : function(){
7592         if(this.rendered){
7593             this.onDisable();
7594         }
7595         this.disabled = true;
7596         this.fireEvent("disable", this);
7597         return this;
7598     },
7599
7600         // private
7601     onDisable : function(){
7602         this.getActionEl().addClass(this.disabledClass);
7603         this.el.dom.disabled = true;
7604     },
7605
7606     /**
7607      * Enable this component.
7608      * @return {Roo.Component} this
7609      */
7610     enable : function(){
7611         if(this.rendered){
7612             this.onEnable();
7613         }
7614         this.disabled = false;
7615         this.fireEvent("enable", this);
7616         return this;
7617     },
7618
7619         // private
7620     onEnable : function(){
7621         this.getActionEl().removeClass(this.disabledClass);
7622         this.el.dom.disabled = false;
7623     },
7624
7625     /**
7626      * Convenience function for setting disabled/enabled by boolean.
7627      * @param {Boolean} disabled
7628      */
7629     setDisabled : function(disabled){
7630         this[disabled ? "disable" : "enable"]();
7631     },
7632
7633     /**
7634      * Show this component.
7635      * @return {Roo.Component} this
7636      */
7637     show: function(){
7638         if(this.fireEvent("beforeshow", this) !== false){
7639             this.hidden = false;
7640             if(this.rendered){
7641                 this.onShow();
7642             }
7643             this.fireEvent("show", this);
7644         }
7645         return this;
7646     },
7647
7648     // private
7649     onShow : function(){
7650         var ae = this.getActionEl();
7651         if(this.hideMode == 'visibility'){
7652             ae.dom.style.visibility = "visible";
7653         }else if(this.hideMode == 'offsets'){
7654             ae.removeClass('x-hidden');
7655         }else{
7656             ae.dom.style.display = "";
7657         }
7658     },
7659
7660     /**
7661      * Hide this component.
7662      * @return {Roo.Component} this
7663      */
7664     hide: function(){
7665         if(this.fireEvent("beforehide", this) !== false){
7666             this.hidden = true;
7667             if(this.rendered){
7668                 this.onHide();
7669             }
7670             this.fireEvent("hide", this);
7671         }
7672         return this;
7673     },
7674
7675     // private
7676     onHide : function(){
7677         var ae = this.getActionEl();
7678         if(this.hideMode == 'visibility'){
7679             ae.dom.style.visibility = "hidden";
7680         }else if(this.hideMode == 'offsets'){
7681             ae.addClass('x-hidden');
7682         }else{
7683             ae.dom.style.display = "none";
7684         }
7685     },
7686
7687     /**
7688      * Convenience function to hide or show this component by boolean.
7689      * @param {Boolean} visible True to show, false to hide
7690      * @return {Roo.Component} this
7691      */
7692     setVisible: function(visible){
7693         if(visible) {
7694             this.show();
7695         }else{
7696             this.hide();
7697         }
7698         return this;
7699     },
7700
7701     /**
7702      * Returns true if this component is visible.
7703      */
7704     isVisible : function(){
7705         return this.getActionEl().isVisible();
7706     },
7707
7708     cloneConfig : function(overrides){
7709         overrides = overrides || {};
7710         var id = overrides.id || Roo.id();
7711         var cfg = Roo.applyIf(overrides, this.initialConfig);
7712         cfg.id = id; // prevent dup id
7713         return new this.constructor(cfg);
7714     }
7715 });/*
7716  * Based on:
7717  * Ext JS Library 1.1.1
7718  * Copyright(c) 2006-2007, Ext JS, LLC.
7719  *
7720  * Originally Released Under LGPL - original licence link has changed is not relivant.
7721  *
7722  * Fork - LGPL
7723  * <script type="text/javascript">
7724  */
7725  (function(){ 
7726 /**
7727  * @class Roo.Layer
7728  * @extends Roo.Element
7729  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7730  * automatic maintaining of shadow/shim positions.
7731  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7732  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7733  * you can pass a string with a CSS class name. False turns off the shadow.
7734  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7735  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7736  * @cfg {String} cls CSS class to add to the element
7737  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7738  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7739  * @constructor
7740  * @param {Object} config An object with config options.
7741  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7742  */
7743
7744 Roo.Layer = function(config, existingEl){
7745     config = config || {};
7746     var dh = Roo.DomHelper;
7747     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7748     if(existingEl){
7749         this.dom = Roo.getDom(existingEl);
7750     }
7751     if(!this.dom){
7752         var o = config.dh || {tag: "div", cls: "x-layer"};
7753         this.dom = dh.append(pel, o);
7754     }
7755     if(config.cls){
7756         this.addClass(config.cls);
7757     }
7758     this.constrain = config.constrain !== false;
7759     this.visibilityMode = Roo.Element.VISIBILITY;
7760     if(config.id){
7761         this.id = this.dom.id = config.id;
7762     }else{
7763         this.id = Roo.id(this.dom);
7764     }
7765     this.zindex = config.zindex || this.getZIndex();
7766     this.position("absolute", this.zindex);
7767     if(config.shadow){
7768         this.shadowOffset = config.shadowOffset || 4;
7769         this.shadow = new Roo.Shadow({
7770             offset : this.shadowOffset,
7771             mode : config.shadow
7772         });
7773     }else{
7774         this.shadowOffset = 0;
7775     }
7776     this.useShim = config.shim !== false && Roo.useShims;
7777     this.useDisplay = config.useDisplay;
7778     this.hide();
7779 };
7780
7781 var supr = Roo.Element.prototype;
7782
7783 // shims are shared among layer to keep from having 100 iframes
7784 var shims = [];
7785
7786 Roo.extend(Roo.Layer, Roo.Element, {
7787
7788     getZIndex : function(){
7789         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7790     },
7791
7792     getShim : function(){
7793         if(!this.useShim){
7794             return null;
7795         }
7796         if(this.shim){
7797             return this.shim;
7798         }
7799         var shim = shims.shift();
7800         if(!shim){
7801             shim = this.createShim();
7802             shim.enableDisplayMode('block');
7803             shim.dom.style.display = 'none';
7804             shim.dom.style.visibility = 'visible';
7805         }
7806         var pn = this.dom.parentNode;
7807         if(shim.dom.parentNode != pn){
7808             pn.insertBefore(shim.dom, this.dom);
7809         }
7810         shim.setStyle('z-index', this.getZIndex()-2);
7811         this.shim = shim;
7812         return shim;
7813     },
7814
7815     hideShim : function(){
7816         if(this.shim){
7817             this.shim.setDisplayed(false);
7818             shims.push(this.shim);
7819             delete this.shim;
7820         }
7821     },
7822
7823     disableShadow : function(){
7824         if(this.shadow){
7825             this.shadowDisabled = true;
7826             this.shadow.hide();
7827             this.lastShadowOffset = this.shadowOffset;
7828             this.shadowOffset = 0;
7829         }
7830     },
7831
7832     enableShadow : function(show){
7833         if(this.shadow){
7834             this.shadowDisabled = false;
7835             this.shadowOffset = this.lastShadowOffset;
7836             delete this.lastShadowOffset;
7837             if(show){
7838                 this.sync(true);
7839             }
7840         }
7841     },
7842
7843     // private
7844     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7845     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7846     sync : function(doShow){
7847         var sw = this.shadow;
7848         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7849             var sh = this.getShim();
7850
7851             var w = this.getWidth(),
7852                 h = this.getHeight();
7853
7854             var l = this.getLeft(true),
7855                 t = this.getTop(true);
7856
7857             if(sw && !this.shadowDisabled){
7858                 if(doShow && !sw.isVisible()){
7859                     sw.show(this);
7860                 }else{
7861                     sw.realign(l, t, w, h);
7862                 }
7863                 if(sh){
7864                     if(doShow){
7865                        sh.show();
7866                     }
7867                     // fit the shim behind the shadow, so it is shimmed too
7868                     var a = sw.adjusts, s = sh.dom.style;
7869                     s.left = (Math.min(l, l+a.l))+"px";
7870                     s.top = (Math.min(t, t+a.t))+"px";
7871                     s.width = (w+a.w)+"px";
7872                     s.height = (h+a.h)+"px";
7873                 }
7874             }else if(sh){
7875                 if(doShow){
7876                    sh.show();
7877                 }
7878                 sh.setSize(w, h);
7879                 sh.setLeftTop(l, t);
7880             }
7881             
7882         }
7883     },
7884
7885     // private
7886     destroy : function(){
7887         this.hideShim();
7888         if(this.shadow){
7889             this.shadow.hide();
7890         }
7891         this.removeAllListeners();
7892         var pn = this.dom.parentNode;
7893         if(pn){
7894             pn.removeChild(this.dom);
7895         }
7896         Roo.Element.uncache(this.id);
7897     },
7898
7899     remove : function(){
7900         this.destroy();
7901     },
7902
7903     // private
7904     beginUpdate : function(){
7905         this.updating = true;
7906     },
7907
7908     // private
7909     endUpdate : function(){
7910         this.updating = false;
7911         this.sync(true);
7912     },
7913
7914     // private
7915     hideUnders : function(negOffset){
7916         if(this.shadow){
7917             this.shadow.hide();
7918         }
7919         this.hideShim();
7920     },
7921
7922     // private
7923     constrainXY : function(){
7924         if(this.constrain){
7925             var vw = Roo.lib.Dom.getViewWidth(),
7926                 vh = Roo.lib.Dom.getViewHeight();
7927             var s = Roo.get(document).getScroll();
7928
7929             var xy = this.getXY();
7930             var x = xy[0], y = xy[1];   
7931             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7932             // only move it if it needs it
7933             var moved = false;
7934             // first validate right/bottom
7935             if((x + w) > vw+s.left){
7936                 x = vw - w - this.shadowOffset;
7937                 moved = true;
7938             }
7939             if((y + h) > vh+s.top){
7940                 y = vh - h - this.shadowOffset;
7941                 moved = true;
7942             }
7943             // then make sure top/left isn't negative
7944             if(x < s.left){
7945                 x = s.left;
7946                 moved = true;
7947             }
7948             if(y < s.top){
7949                 y = s.top;
7950                 moved = true;
7951             }
7952             if(moved){
7953                 if(this.avoidY){
7954                     var ay = this.avoidY;
7955                     if(y <= ay && (y+h) >= ay){
7956                         y = ay-h-5;   
7957                     }
7958                 }
7959                 xy = [x, y];
7960                 this.storeXY(xy);
7961                 supr.setXY.call(this, xy);
7962                 this.sync();
7963             }
7964         }
7965     },
7966
7967     isVisible : function(){
7968         return this.visible;    
7969     },
7970
7971     // private
7972     showAction : function(){
7973         this.visible = true; // track visibility to prevent getStyle calls
7974         if(this.useDisplay === true){
7975             this.setDisplayed("");
7976         }else if(this.lastXY){
7977             supr.setXY.call(this, this.lastXY);
7978         }else if(this.lastLT){
7979             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7980         }
7981     },
7982
7983     // private
7984     hideAction : function(){
7985         this.visible = false;
7986         if(this.useDisplay === true){
7987             this.setDisplayed(false);
7988         }else{
7989             this.setLeftTop(-10000,-10000);
7990         }
7991     },
7992
7993     // overridden Element method
7994     setVisible : function(v, a, d, c, e){
7995         if(v){
7996             this.showAction();
7997         }
7998         if(a && v){
7999             var cb = function(){
8000                 this.sync(true);
8001                 if(c){
8002                     c();
8003                 }
8004             }.createDelegate(this);
8005             supr.setVisible.call(this, true, true, d, cb, e);
8006         }else{
8007             if(!v){
8008                 this.hideUnders(true);
8009             }
8010             var cb = c;
8011             if(a){
8012                 cb = function(){
8013                     this.hideAction();
8014                     if(c){
8015                         c();
8016                     }
8017                 }.createDelegate(this);
8018             }
8019             supr.setVisible.call(this, v, a, d, cb, e);
8020             if(v){
8021                 this.sync(true);
8022             }else if(!a){
8023                 this.hideAction();
8024             }
8025         }
8026     },
8027
8028     storeXY : function(xy){
8029         delete this.lastLT;
8030         this.lastXY = xy;
8031     },
8032
8033     storeLeftTop : function(left, top){
8034         delete this.lastXY;
8035         this.lastLT = [left, top];
8036     },
8037
8038     // private
8039     beforeFx : function(){
8040         this.beforeAction();
8041         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8042     },
8043
8044     // private
8045     afterFx : function(){
8046         Roo.Layer.superclass.afterFx.apply(this, arguments);
8047         this.sync(this.isVisible());
8048     },
8049
8050     // private
8051     beforeAction : function(){
8052         if(!this.updating && this.shadow){
8053             this.shadow.hide();
8054         }
8055     },
8056
8057     // overridden Element method
8058     setLeft : function(left){
8059         this.storeLeftTop(left, this.getTop(true));
8060         supr.setLeft.apply(this, arguments);
8061         this.sync();
8062     },
8063
8064     setTop : function(top){
8065         this.storeLeftTop(this.getLeft(true), top);
8066         supr.setTop.apply(this, arguments);
8067         this.sync();
8068     },
8069
8070     setLeftTop : function(left, top){
8071         this.storeLeftTop(left, top);
8072         supr.setLeftTop.apply(this, arguments);
8073         this.sync();
8074     },
8075
8076     setXY : function(xy, a, d, c, e){
8077         this.fixDisplay();
8078         this.beforeAction();
8079         this.storeXY(xy);
8080         var cb = this.createCB(c);
8081         supr.setXY.call(this, xy, a, d, cb, e);
8082         if(!a){
8083             cb();
8084         }
8085     },
8086
8087     // private
8088     createCB : function(c){
8089         var el = this;
8090         return function(){
8091             el.constrainXY();
8092             el.sync(true);
8093             if(c){
8094                 c();
8095             }
8096         };
8097     },
8098
8099     // overridden Element method
8100     setX : function(x, a, d, c, e){
8101         this.setXY([x, this.getY()], a, d, c, e);
8102     },
8103
8104     // overridden Element method
8105     setY : function(y, a, d, c, e){
8106         this.setXY([this.getX(), y], a, d, c, e);
8107     },
8108
8109     // overridden Element method
8110     setSize : function(w, h, a, d, c, e){
8111         this.beforeAction();
8112         var cb = this.createCB(c);
8113         supr.setSize.call(this, w, h, a, d, cb, e);
8114         if(!a){
8115             cb();
8116         }
8117     },
8118
8119     // overridden Element method
8120     setWidth : function(w, a, d, c, e){
8121         this.beforeAction();
8122         var cb = this.createCB(c);
8123         supr.setWidth.call(this, w, a, d, cb, e);
8124         if(!a){
8125             cb();
8126         }
8127     },
8128
8129     // overridden Element method
8130     setHeight : function(h, a, d, c, e){
8131         this.beforeAction();
8132         var cb = this.createCB(c);
8133         supr.setHeight.call(this, h, a, d, cb, e);
8134         if(!a){
8135             cb();
8136         }
8137     },
8138
8139     // overridden Element method
8140     setBounds : function(x, y, w, h, a, d, c, e){
8141         this.beforeAction();
8142         var cb = this.createCB(c);
8143         if(!a){
8144             this.storeXY([x, y]);
8145             supr.setXY.call(this, [x, y]);
8146             supr.setSize.call(this, w, h, a, d, cb, e);
8147             cb();
8148         }else{
8149             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8150         }
8151         return this;
8152     },
8153     
8154     /**
8155      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8156      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8157      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8158      * @param {Number} zindex The new z-index to set
8159      * @return {this} The Layer
8160      */
8161     setZIndex : function(zindex){
8162         this.zindex = zindex;
8163         this.setStyle("z-index", zindex + 2);
8164         if(this.shadow){
8165             this.shadow.setZIndex(zindex + 1);
8166         }
8167         if(this.shim){
8168             this.shim.setStyle("z-index", zindex);
8169         }
8170     }
8171 });
8172 })();/*
8173  * Based on:
8174  * Ext JS Library 1.1.1
8175  * Copyright(c) 2006-2007, Ext JS, LLC.
8176  *
8177  * Originally Released Under LGPL - original licence link has changed is not relivant.
8178  *
8179  * Fork - LGPL
8180  * <script type="text/javascript">
8181  */
8182
8183
8184 /**
8185  * @class Roo.Shadow
8186  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8187  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8188  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8189  * @constructor
8190  * Create a new Shadow
8191  * @param {Object} config The config object
8192  */
8193 Roo.Shadow = function(config){
8194     Roo.apply(this, config);
8195     if(typeof this.mode != "string"){
8196         this.mode = this.defaultMode;
8197     }
8198     var o = this.offset, a = {h: 0};
8199     var rad = Math.floor(this.offset/2);
8200     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8201         case "drop":
8202             a.w = 0;
8203             a.l = a.t = o;
8204             a.t -= 1;
8205             if(Roo.isIE){
8206                 a.l -= this.offset + rad;
8207                 a.t -= this.offset + rad;
8208                 a.w -= rad;
8209                 a.h -= rad;
8210                 a.t += 1;
8211             }
8212         break;
8213         case "sides":
8214             a.w = (o*2);
8215             a.l = -o;
8216             a.t = o-1;
8217             if(Roo.isIE){
8218                 a.l -= (this.offset - rad);
8219                 a.t -= this.offset + rad;
8220                 a.l += 1;
8221                 a.w -= (this.offset - rad)*2;
8222                 a.w -= rad + 1;
8223                 a.h -= 1;
8224             }
8225         break;
8226         case "frame":
8227             a.w = a.h = (o*2);
8228             a.l = a.t = -o;
8229             a.t += 1;
8230             a.h -= 2;
8231             if(Roo.isIE){
8232                 a.l -= (this.offset - rad);
8233                 a.t -= (this.offset - rad);
8234                 a.l += 1;
8235                 a.w -= (this.offset + rad + 1);
8236                 a.h -= (this.offset + rad);
8237                 a.h += 1;
8238             }
8239         break;
8240     };
8241
8242     this.adjusts = a;
8243 };
8244
8245 Roo.Shadow.prototype = {
8246     /**
8247      * @cfg {String} mode
8248      * The shadow display mode.  Supports the following options:<br />
8249      * sides: Shadow displays on both sides and bottom only<br />
8250      * frame: Shadow displays equally on all four sides<br />
8251      * drop: Traditional bottom-right drop shadow (default)
8252      */
8253     /**
8254      * @cfg {String} offset
8255      * The number of pixels to offset the shadow from the element (defaults to 4)
8256      */
8257     offset: 4,
8258
8259     // private
8260     defaultMode: "drop",
8261
8262     /**
8263      * Displays the shadow under the target element
8264      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8265      */
8266     show : function(target){
8267         target = Roo.get(target);
8268         if(!this.el){
8269             this.el = Roo.Shadow.Pool.pull();
8270             if(this.el.dom.nextSibling != target.dom){
8271                 this.el.insertBefore(target);
8272             }
8273         }
8274         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8275         if(Roo.isIE){
8276             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8277         }
8278         this.realign(
8279             target.getLeft(true),
8280             target.getTop(true),
8281             target.getWidth(),
8282             target.getHeight()
8283         );
8284         this.el.dom.style.display = "block";
8285     },
8286
8287     /**
8288      * Returns true if the shadow is visible, else false
8289      */
8290     isVisible : function(){
8291         return this.el ? true : false;  
8292     },
8293
8294     /**
8295      * Direct alignment when values are already available. Show must be called at least once before
8296      * calling this method to ensure it is initialized.
8297      * @param {Number} left The target element left position
8298      * @param {Number} top The target element top position
8299      * @param {Number} width The target element width
8300      * @param {Number} height The target element height
8301      */
8302     realign : function(l, t, w, h){
8303         if(!this.el){
8304             return;
8305         }
8306         var a = this.adjusts, d = this.el.dom, s = d.style;
8307         var iea = 0;
8308         s.left = (l+a.l)+"px";
8309         s.top = (t+a.t)+"px";
8310         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8311  
8312         if(s.width != sws || s.height != shs){
8313             s.width = sws;
8314             s.height = shs;
8315             if(!Roo.isIE){
8316                 var cn = d.childNodes;
8317                 var sww = Math.max(0, (sw-12))+"px";
8318                 cn[0].childNodes[1].style.width = sww;
8319                 cn[1].childNodes[1].style.width = sww;
8320                 cn[2].childNodes[1].style.width = sww;
8321                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8322             }
8323         }
8324     },
8325
8326     /**
8327      * Hides this shadow
8328      */
8329     hide : function(){
8330         if(this.el){
8331             this.el.dom.style.display = "none";
8332             Roo.Shadow.Pool.push(this.el);
8333             delete this.el;
8334         }
8335     },
8336
8337     /**
8338      * Adjust the z-index of this shadow
8339      * @param {Number} zindex The new z-index
8340      */
8341     setZIndex : function(z){
8342         this.zIndex = z;
8343         if(this.el){
8344             this.el.setStyle("z-index", z);
8345         }
8346     }
8347 };
8348
8349 // Private utility class that manages the internal Shadow cache
8350 Roo.Shadow.Pool = function(){
8351     var p = [];
8352     var markup = Roo.isIE ?
8353                  '<div class="x-ie-shadow"></div>' :
8354                  '<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>';
8355     return {
8356         pull : function(){
8357             var sh = p.shift();
8358             if(!sh){
8359                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8360                 sh.autoBoxAdjust = false;
8361             }
8362             return sh;
8363         },
8364
8365         push : function(sh){
8366             p.push(sh);
8367         }
8368     };
8369 }();/*
8370  * Based on:
8371  * Ext JS Library 1.1.1
8372  * Copyright(c) 2006-2007, Ext JS, LLC.
8373  *
8374  * Originally Released Under LGPL - original licence link has changed is not relivant.
8375  *
8376  * Fork - LGPL
8377  * <script type="text/javascript">
8378  */
8379
8380 /**
8381  * @class Roo.BoxComponent
8382  * @extends Roo.Component
8383  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8384  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8385  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8386  * layout containers.
8387  * @constructor
8388  * @param {Roo.Element/String/Object} config The configuration options.
8389  */
8390 Roo.BoxComponent = function(config){
8391     Roo.Component.call(this, config);
8392     this.addEvents({
8393         /**
8394          * @event resize
8395          * Fires after the component is resized.
8396              * @param {Roo.Component} this
8397              * @param {Number} adjWidth The box-adjusted width that was set
8398              * @param {Number} adjHeight The box-adjusted height that was set
8399              * @param {Number} rawWidth The width that was originally specified
8400              * @param {Number} rawHeight The height that was originally specified
8401              */
8402         resize : true,
8403         /**
8404          * @event move
8405          * Fires after the component is moved.
8406              * @param {Roo.Component} this
8407              * @param {Number} x The new x position
8408              * @param {Number} y The new y position
8409              */
8410         move : true
8411     });
8412 };
8413
8414 Roo.extend(Roo.BoxComponent, Roo.Component, {
8415     // private, set in afterRender to signify that the component has been rendered
8416     boxReady : false,
8417     // private, used to defer height settings to subclasses
8418     deferHeight: false,
8419     /** @cfg {Number} width
8420      * width (optional) size of component
8421      */
8422      /** @cfg {Number} height
8423      * height (optional) size of component
8424      */
8425      
8426     /**
8427      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8428      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8429      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8430      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8431      * @return {Roo.BoxComponent} this
8432      */
8433     setSize : function(w, h){
8434         // support for standard size objects
8435         if(typeof w == 'object'){
8436             h = w.height;
8437             w = w.width;
8438         }
8439         // not rendered
8440         if(!this.boxReady){
8441             this.width = w;
8442             this.height = h;
8443             return this;
8444         }
8445
8446         // prevent recalcs when not needed
8447         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8448             return this;
8449         }
8450         this.lastSize = {width: w, height: h};
8451
8452         var adj = this.adjustSize(w, h);
8453         var aw = adj.width, ah = adj.height;
8454         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8455             var rz = this.getResizeEl();
8456             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8457                 rz.setSize(aw, ah);
8458             }else if(!this.deferHeight && ah !== undefined){
8459                 rz.setHeight(ah);
8460             }else if(aw !== undefined){
8461                 rz.setWidth(aw);
8462             }
8463             this.onResize(aw, ah, w, h);
8464             this.fireEvent('resize', this, aw, ah, w, h);
8465         }
8466         return this;
8467     },
8468
8469     /**
8470      * Gets the current size of the component's underlying element.
8471      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8472      */
8473     getSize : function(){
8474         return this.el.getSize();
8475     },
8476
8477     /**
8478      * Gets the current XY position of the component's underlying element.
8479      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8480      * @return {Array} The XY position of the element (e.g., [100, 200])
8481      */
8482     getPosition : function(local){
8483         if(local === true){
8484             return [this.el.getLeft(true), this.el.getTop(true)];
8485         }
8486         return this.xy || this.el.getXY();
8487     },
8488
8489     /**
8490      * Gets the current box measurements of the component's underlying element.
8491      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8492      * @returns {Object} box An object in the format {x, y, width, height}
8493      */
8494     getBox : function(local){
8495         var s = this.el.getSize();
8496         if(local){
8497             s.x = this.el.getLeft(true);
8498             s.y = this.el.getTop(true);
8499         }else{
8500             var xy = this.xy || this.el.getXY();
8501             s.x = xy[0];
8502             s.y = xy[1];
8503         }
8504         return s;
8505     },
8506
8507     /**
8508      * Sets the current box measurements of the component's underlying element.
8509      * @param {Object} box An object in the format {x, y, width, height}
8510      * @returns {Roo.BoxComponent} this
8511      */
8512     updateBox : function(box){
8513         this.setSize(box.width, box.height);
8514         this.setPagePosition(box.x, box.y);
8515         return this;
8516     },
8517
8518     // protected
8519     getResizeEl : function(){
8520         return this.resizeEl || this.el;
8521     },
8522
8523     // protected
8524     getPositionEl : function(){
8525         return this.positionEl || this.el;
8526     },
8527
8528     /**
8529      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8530      * This method fires the move event.
8531      * @param {Number} left The new left
8532      * @param {Number} top The new top
8533      * @returns {Roo.BoxComponent} this
8534      */
8535     setPosition : function(x, y){
8536         this.x = x;
8537         this.y = y;
8538         if(!this.boxReady){
8539             return this;
8540         }
8541         var adj = this.adjustPosition(x, y);
8542         var ax = adj.x, ay = adj.y;
8543
8544         var el = this.getPositionEl();
8545         if(ax !== undefined || ay !== undefined){
8546             if(ax !== undefined && ay !== undefined){
8547                 el.setLeftTop(ax, ay);
8548             }else if(ax !== undefined){
8549                 el.setLeft(ax);
8550             }else if(ay !== undefined){
8551                 el.setTop(ay);
8552             }
8553             this.onPosition(ax, ay);
8554             this.fireEvent('move', this, ax, ay);
8555         }
8556         return this;
8557     },
8558
8559     /**
8560      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8561      * This method fires the move event.
8562      * @param {Number} x The new x position
8563      * @param {Number} y The new y position
8564      * @returns {Roo.BoxComponent} this
8565      */
8566     setPagePosition : function(x, y){
8567         this.pageX = x;
8568         this.pageY = y;
8569         if(!this.boxReady){
8570             return;
8571         }
8572         if(x === undefined || y === undefined){ // cannot translate undefined points
8573             return;
8574         }
8575         var p = this.el.translatePoints(x, y);
8576         this.setPosition(p.left, p.top);
8577         return this;
8578     },
8579
8580     // private
8581     onRender : function(ct, position){
8582         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8583         if(this.resizeEl){
8584             this.resizeEl = Roo.get(this.resizeEl);
8585         }
8586         if(this.positionEl){
8587             this.positionEl = Roo.get(this.positionEl);
8588         }
8589     },
8590
8591     // private
8592     afterRender : function(){
8593         Roo.BoxComponent.superclass.afterRender.call(this);
8594         this.boxReady = true;
8595         this.setSize(this.width, this.height);
8596         if(this.x || this.y){
8597             this.setPosition(this.x, this.y);
8598         }
8599         if(this.pageX || this.pageY){
8600             this.setPagePosition(this.pageX, this.pageY);
8601         }
8602     },
8603
8604     /**
8605      * Force the component's size to recalculate based on the underlying element's current height and width.
8606      * @returns {Roo.BoxComponent} this
8607      */
8608     syncSize : function(){
8609         delete this.lastSize;
8610         this.setSize(this.el.getWidth(), this.el.getHeight());
8611         return this;
8612     },
8613
8614     /**
8615      * Called after the component is resized, this method is empty by default but can be implemented by any
8616      * subclass that needs to perform custom logic after a resize occurs.
8617      * @param {Number} adjWidth The box-adjusted width that was set
8618      * @param {Number} adjHeight The box-adjusted height that was set
8619      * @param {Number} rawWidth The width that was originally specified
8620      * @param {Number} rawHeight The height that was originally specified
8621      */
8622     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8623
8624     },
8625
8626     /**
8627      * Called after the component is moved, this method is empty by default but can be implemented by any
8628      * subclass that needs to perform custom logic after a move occurs.
8629      * @param {Number} x The new x position
8630      * @param {Number} y The new y position
8631      */
8632     onPosition : function(x, y){
8633
8634     },
8635
8636     // private
8637     adjustSize : function(w, h){
8638         if(this.autoWidth){
8639             w = 'auto';
8640         }
8641         if(this.autoHeight){
8642             h = 'auto';
8643         }
8644         return {width : w, height: h};
8645     },
8646
8647     // private
8648     adjustPosition : function(x, y){
8649         return {x : x, y: y};
8650     }
8651 });/*
8652  * Based on:
8653  * Ext JS Library 1.1.1
8654  * Copyright(c) 2006-2007, Ext JS, LLC.
8655  *
8656  * Originally Released Under LGPL - original licence link has changed is not relivant.
8657  *
8658  * Fork - LGPL
8659  * <script type="text/javascript">
8660  */
8661
8662
8663 /**
8664  * @class Roo.SplitBar
8665  * @extends Roo.util.Observable
8666  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8667  * <br><br>
8668  * Usage:
8669  * <pre><code>
8670 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8671                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8672 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8673 split.minSize = 100;
8674 split.maxSize = 600;
8675 split.animate = true;
8676 split.on('moved', splitterMoved);
8677 </code></pre>
8678  * @constructor
8679  * Create a new SplitBar
8680  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8681  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8682  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8683  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8684                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8685                         position of the SplitBar).
8686  */
8687 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8688     
8689     /** @private */
8690     this.el = Roo.get(dragElement, true);
8691     this.el.dom.unselectable = "on";
8692     /** @private */
8693     this.resizingEl = Roo.get(resizingElement, true);
8694
8695     /**
8696      * @private
8697      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8698      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8699      * @type Number
8700      */
8701     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8702     
8703     /**
8704      * The minimum size of the resizing element. (Defaults to 0)
8705      * @type Number
8706      */
8707     this.minSize = 0;
8708     
8709     /**
8710      * The maximum size of the resizing element. (Defaults to 2000)
8711      * @type Number
8712      */
8713     this.maxSize = 2000;
8714     
8715     /**
8716      * Whether to animate the transition to the new size
8717      * @type Boolean
8718      */
8719     this.animate = false;
8720     
8721     /**
8722      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8723      * @type Boolean
8724      */
8725     this.useShim = false;
8726     
8727     /** @private */
8728     this.shim = null;
8729     
8730     if(!existingProxy){
8731         /** @private */
8732         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8733     }else{
8734         this.proxy = Roo.get(existingProxy).dom;
8735     }
8736     /** @private */
8737     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8738     
8739     /** @private */
8740     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8741     
8742     /** @private */
8743     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8744     
8745     /** @private */
8746     this.dragSpecs = {};
8747     
8748     /**
8749      * @private The adapter to use to positon and resize elements
8750      */
8751     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8752     this.adapter.init(this);
8753     
8754     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8755         /** @private */
8756         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8757         this.el.addClass("x-splitbar-h");
8758     }else{
8759         /** @private */
8760         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8761         this.el.addClass("x-splitbar-v");
8762     }
8763     
8764     this.addEvents({
8765         /**
8766          * @event resize
8767          * Fires when the splitter is moved (alias for {@link #event-moved})
8768          * @param {Roo.SplitBar} this
8769          * @param {Number} newSize the new width or height
8770          */
8771         "resize" : true,
8772         /**
8773          * @event moved
8774          * Fires when the splitter is moved
8775          * @param {Roo.SplitBar} this
8776          * @param {Number} newSize the new width or height
8777          */
8778         "moved" : true,
8779         /**
8780          * @event beforeresize
8781          * Fires before the splitter is dragged
8782          * @param {Roo.SplitBar} this
8783          */
8784         "beforeresize" : true,
8785
8786         "beforeapply" : true
8787     });
8788
8789     Roo.util.Observable.call(this);
8790 };
8791
8792 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8793     onStartProxyDrag : function(x, y){
8794         this.fireEvent("beforeresize", this);
8795         if(!this.overlay){
8796             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8797             o.unselectable();
8798             o.enableDisplayMode("block");
8799             // all splitbars share the same overlay
8800             Roo.SplitBar.prototype.overlay = o;
8801         }
8802         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8803         this.overlay.show();
8804         Roo.get(this.proxy).setDisplayed("block");
8805         var size = this.adapter.getElementSize(this);
8806         this.activeMinSize = this.getMinimumSize();;
8807         this.activeMaxSize = this.getMaximumSize();;
8808         var c1 = size - this.activeMinSize;
8809         var c2 = Math.max(this.activeMaxSize - size, 0);
8810         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8811             this.dd.resetConstraints();
8812             this.dd.setXConstraint(
8813                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8814                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8815             );
8816             this.dd.setYConstraint(0, 0);
8817         }else{
8818             this.dd.resetConstraints();
8819             this.dd.setXConstraint(0, 0);
8820             this.dd.setYConstraint(
8821                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8822                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8823             );
8824          }
8825         this.dragSpecs.startSize = size;
8826         this.dragSpecs.startPoint = [x, y];
8827         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8828     },
8829     
8830     /** 
8831      * @private Called after the drag operation by the DDProxy
8832      */
8833     onEndProxyDrag : function(e){
8834         Roo.get(this.proxy).setDisplayed(false);
8835         var endPoint = Roo.lib.Event.getXY(e);
8836         if(this.overlay){
8837             this.overlay.hide();
8838         }
8839         var newSize;
8840         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8841             newSize = this.dragSpecs.startSize + 
8842                 (this.placement == Roo.SplitBar.LEFT ?
8843                     endPoint[0] - this.dragSpecs.startPoint[0] :
8844                     this.dragSpecs.startPoint[0] - endPoint[0]
8845                 );
8846         }else{
8847             newSize = this.dragSpecs.startSize + 
8848                 (this.placement == Roo.SplitBar.TOP ?
8849                     endPoint[1] - this.dragSpecs.startPoint[1] :
8850                     this.dragSpecs.startPoint[1] - endPoint[1]
8851                 );
8852         }
8853         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8854         if(newSize != this.dragSpecs.startSize){
8855             if(this.fireEvent('beforeapply', this, newSize) !== false){
8856                 this.adapter.setElementSize(this, newSize);
8857                 this.fireEvent("moved", this, newSize);
8858                 this.fireEvent("resize", this, newSize);
8859             }
8860         }
8861     },
8862     
8863     /**
8864      * Get the adapter this SplitBar uses
8865      * @return The adapter object
8866      */
8867     getAdapter : function(){
8868         return this.adapter;
8869     },
8870     
8871     /**
8872      * Set the adapter this SplitBar uses
8873      * @param {Object} adapter A SplitBar adapter object
8874      */
8875     setAdapter : function(adapter){
8876         this.adapter = adapter;
8877         this.adapter.init(this);
8878     },
8879     
8880     /**
8881      * Gets the minimum size for the resizing element
8882      * @return {Number} The minimum size
8883      */
8884     getMinimumSize : function(){
8885         return this.minSize;
8886     },
8887     
8888     /**
8889      * Sets the minimum size for the resizing element
8890      * @param {Number} minSize The minimum size
8891      */
8892     setMinimumSize : function(minSize){
8893         this.minSize = minSize;
8894     },
8895     
8896     /**
8897      * Gets the maximum size for the resizing element
8898      * @return {Number} The maximum size
8899      */
8900     getMaximumSize : function(){
8901         return this.maxSize;
8902     },
8903     
8904     /**
8905      * Sets the maximum size for the resizing element
8906      * @param {Number} maxSize The maximum size
8907      */
8908     setMaximumSize : function(maxSize){
8909         this.maxSize = maxSize;
8910     },
8911     
8912     /**
8913      * Sets the initialize size for the resizing element
8914      * @param {Number} size The initial size
8915      */
8916     setCurrentSize : function(size){
8917         var oldAnimate = this.animate;
8918         this.animate = false;
8919         this.adapter.setElementSize(this, size);
8920         this.animate = oldAnimate;
8921     },
8922     
8923     /**
8924      * Destroy this splitbar. 
8925      * @param {Boolean} removeEl True to remove the element
8926      */
8927     destroy : function(removeEl){
8928         if(this.shim){
8929             this.shim.remove();
8930         }
8931         this.dd.unreg();
8932         this.proxy.parentNode.removeChild(this.proxy);
8933         if(removeEl){
8934             this.el.remove();
8935         }
8936     }
8937 });
8938
8939 /**
8940  * @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.
8941  */
8942 Roo.SplitBar.createProxy = function(dir){
8943     var proxy = new Roo.Element(document.createElement("div"));
8944     proxy.unselectable();
8945     var cls = 'x-splitbar-proxy';
8946     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8947     document.body.appendChild(proxy.dom);
8948     return proxy.dom;
8949 };
8950
8951 /** 
8952  * @class Roo.SplitBar.BasicLayoutAdapter
8953  * Default Adapter. It assumes the splitter and resizing element are not positioned
8954  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8955  */
8956 Roo.SplitBar.BasicLayoutAdapter = function(){
8957 };
8958
8959 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8960     // do nothing for now
8961     init : function(s){
8962     
8963     },
8964     /**
8965      * Called before drag operations to get the current size of the resizing element. 
8966      * @param {Roo.SplitBar} s The SplitBar using this adapter
8967      */
8968      getElementSize : function(s){
8969         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8970             return s.resizingEl.getWidth();
8971         }else{
8972             return s.resizingEl.getHeight();
8973         }
8974     },
8975     
8976     /**
8977      * Called after drag operations to set the size of the resizing element.
8978      * @param {Roo.SplitBar} s The SplitBar using this adapter
8979      * @param {Number} newSize The new size to set
8980      * @param {Function} onComplete A function to be invoked when resizing is complete
8981      */
8982     setElementSize : function(s, newSize, onComplete){
8983         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8984             if(!s.animate){
8985                 s.resizingEl.setWidth(newSize);
8986                 if(onComplete){
8987                     onComplete(s, newSize);
8988                 }
8989             }else{
8990                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8991             }
8992         }else{
8993             
8994             if(!s.animate){
8995                 s.resizingEl.setHeight(newSize);
8996                 if(onComplete){
8997                     onComplete(s, newSize);
8998                 }
8999             }else{
9000                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9001             }
9002         }
9003     }
9004 };
9005
9006 /** 
9007  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9008  * @extends Roo.SplitBar.BasicLayoutAdapter
9009  * Adapter that  moves the splitter element to align with the resized sizing element. 
9010  * Used with an absolute positioned SplitBar.
9011  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9012  * document.body, make sure you assign an id to the body element.
9013  */
9014 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9015     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9016     this.container = Roo.get(container);
9017 };
9018
9019 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9020     init : function(s){
9021         this.basic.init(s);
9022     },
9023     
9024     getElementSize : function(s){
9025         return this.basic.getElementSize(s);
9026     },
9027     
9028     setElementSize : function(s, newSize, onComplete){
9029         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9030     },
9031     
9032     moveSplitter : function(s){
9033         var yes = Roo.SplitBar;
9034         switch(s.placement){
9035             case yes.LEFT:
9036                 s.el.setX(s.resizingEl.getRight());
9037                 break;
9038             case yes.RIGHT:
9039                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9040                 break;
9041             case yes.TOP:
9042                 s.el.setY(s.resizingEl.getBottom());
9043                 break;
9044             case yes.BOTTOM:
9045                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9046                 break;
9047         }
9048     }
9049 };
9050
9051 /**
9052  * Orientation constant - Create a vertical SplitBar
9053  * @static
9054  * @type Number
9055  */
9056 Roo.SplitBar.VERTICAL = 1;
9057
9058 /**
9059  * Orientation constant - Create a horizontal SplitBar
9060  * @static
9061  * @type Number
9062  */
9063 Roo.SplitBar.HORIZONTAL = 2;
9064
9065 /**
9066  * Placement constant - The resizing element is to the left of the splitter element
9067  * @static
9068  * @type Number
9069  */
9070 Roo.SplitBar.LEFT = 1;
9071
9072 /**
9073  * Placement constant - The resizing element is to the right of the splitter element
9074  * @static
9075  * @type Number
9076  */
9077 Roo.SplitBar.RIGHT = 2;
9078
9079 /**
9080  * Placement constant - The resizing element is positioned above the splitter element
9081  * @static
9082  * @type Number
9083  */
9084 Roo.SplitBar.TOP = 3;
9085
9086 /**
9087  * Placement constant - The resizing element is positioned under splitter element
9088  * @static
9089  * @type Number
9090  */
9091 Roo.SplitBar.BOTTOM = 4;
9092 /*
9093  * Based on:
9094  * Ext JS Library 1.1.1
9095  * Copyright(c) 2006-2007, Ext JS, LLC.
9096  *
9097  * Originally Released Under LGPL - original licence link has changed is not relivant.
9098  *
9099  * Fork - LGPL
9100  * <script type="text/javascript">
9101  */
9102
9103 /**
9104  * @class Roo.View
9105  * @extends Roo.util.Observable
9106  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9107  * This class also supports single and multi selection modes. <br>
9108  * Create a data model bound view:
9109  <pre><code>
9110  var store = new Roo.data.Store(...);
9111
9112  var view = new Roo.View({
9113     el : "my-element",
9114     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9115  
9116     singleSelect: true,
9117     selectedClass: "ydataview-selected",
9118     store: store
9119  });
9120
9121  // listen for node click?
9122  view.on("click", function(vw, index, node, e){
9123  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9124  });
9125
9126  // load XML data
9127  dataModel.load("foobar.xml");
9128  </code></pre>
9129  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9130  * <br><br>
9131  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9132  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9133  * 
9134  * Note: old style constructor is still suported (container, template, config)
9135  * 
9136  * @constructor
9137  * Create a new View
9138  * @param {Object} config The config object
9139  * 
9140  */
9141 Roo.View = function(config, depreciated_tpl, depreciated_config){
9142     
9143     if (typeof(depreciated_tpl) == 'undefined') {
9144         // new way.. - universal constructor.
9145         Roo.apply(this, config);
9146         this.el  = Roo.get(this.el);
9147     } else {
9148         // old format..
9149         this.el  = Roo.get(config);
9150         this.tpl = depreciated_tpl;
9151         Roo.apply(this, depreciated_config);
9152     }
9153      
9154     
9155     if(typeof(this.tpl) == "string"){
9156         this.tpl = new Roo.Template(this.tpl);
9157     } else {
9158         // support xtype ctors..
9159         this.tpl = new Roo.factory(this.tpl, Roo);
9160     }
9161     
9162     
9163     this.tpl.compile();
9164    
9165
9166      
9167     /** @private */
9168     this.addEvents({
9169         /**
9170          * @event beforeclick
9171          * Fires before a click is processed. Returns false to cancel the default action.
9172          * @param {Roo.View} this
9173          * @param {Number} index The index of the target node
9174          * @param {HTMLElement} node The target node
9175          * @param {Roo.EventObject} e The raw event object
9176          */
9177             "beforeclick" : true,
9178         /**
9179          * @event click
9180          * Fires when a template node is clicked.
9181          * @param {Roo.View} this
9182          * @param {Number} index The index of the target node
9183          * @param {HTMLElement} node The target node
9184          * @param {Roo.EventObject} e The raw event object
9185          */
9186             "click" : true,
9187         /**
9188          * @event dblclick
9189          * Fires when a template node is double clicked.
9190          * @param {Roo.View} this
9191          * @param {Number} index The index of the target node
9192          * @param {HTMLElement} node The target node
9193          * @param {Roo.EventObject} e The raw event object
9194          */
9195             "dblclick" : true,
9196         /**
9197          * @event contextmenu
9198          * Fires when a template node is right clicked.
9199          * @param {Roo.View} this
9200          * @param {Number} index The index of the target node
9201          * @param {HTMLElement} node The target node
9202          * @param {Roo.EventObject} e The raw event object
9203          */
9204             "contextmenu" : true,
9205         /**
9206          * @event selectionchange
9207          * Fires when the selected nodes change.
9208          * @param {Roo.View} this
9209          * @param {Array} selections Array of the selected nodes
9210          */
9211             "selectionchange" : true,
9212     
9213         /**
9214          * @event beforeselect
9215          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9216          * @param {Roo.View} this
9217          * @param {HTMLElement} node The node to be selected
9218          * @param {Array} selections Array of currently selected nodes
9219          */
9220             "beforeselect" : true,
9221         /**
9222          * @event preparedata
9223          * Fires on every row to render, to allow you to change the data.
9224          * @param {Roo.View} this
9225          * @param {Object} data to be rendered (change this)
9226          */
9227           "preparedata" : true
9228         });
9229
9230     this.el.on({
9231         "click": this.onClick,
9232         "dblclick": this.onDblClick,
9233         "contextmenu": this.onContextMenu,
9234         scope:this
9235     });
9236
9237     this.selections = [];
9238     this.nodes = [];
9239     this.cmp = new Roo.CompositeElementLite([]);
9240     if(this.store){
9241         this.store = Roo.factory(this.store, Roo.data);
9242         this.setStore(this.store, true);
9243     }
9244     Roo.View.superclass.constructor.call(this);
9245 };
9246
9247 Roo.extend(Roo.View, Roo.util.Observable, {
9248     
9249      /**
9250      * @cfg {Roo.data.Store} store Data store to load data from.
9251      */
9252     store : false,
9253     
9254     /**
9255      * @cfg {String|Roo.Element} el The container element.
9256      */
9257     el : '',
9258     
9259     /**
9260      * @cfg {String|Roo.Template} tpl The template used by this View 
9261      */
9262     tpl : false,
9263     /**
9264      * @cfg {String} dataName the named area of the template to use as the data area
9265      *                          Works with domtemplates roo-name="name"
9266      */
9267     dataName: false,
9268     /**
9269      * @cfg {String} selectedClass The css class to add to selected nodes
9270      */
9271     selectedClass : "x-view-selected",
9272      /**
9273      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9274      */
9275     emptyText : "",
9276     /**
9277      * @cfg {Boolean} multiSelect Allow multiple selection
9278      */
9279     multiSelect : false,
9280     /**
9281      * @cfg {Boolean} singleSelect Allow single selection
9282      */
9283     singleSelect:  false,
9284     
9285     /**
9286      * @cfg {Boolean} toggleSelect - selecting 
9287      */
9288     toggleSelect : false,
9289     
9290     /**
9291      * Returns the element this view is bound to.
9292      * @return {Roo.Element}
9293      */
9294     getEl : function(){
9295         return this.el;
9296     },
9297
9298     /**
9299      * Refreshes the view.
9300      */
9301     refresh : function(){
9302         var t = this.tpl;
9303         
9304         // if we are using something like 'domtemplate', then
9305         // the what gets used is:
9306         // t.applySubtemplate(NAME, data, wrapping data..)
9307         // the outer template then get' applied with
9308         //     the store 'extra data'
9309         // and the body get's added to the
9310         //      roo-name="data" node?
9311         //      <span class='roo-tpl-{name}'></span> ?????
9312         
9313         
9314         
9315         this.clearSelections();
9316         this.el.update("");
9317         var html = [];
9318         var records = this.store.getRange();
9319         if(records.length < 1) {
9320             
9321             // is this valid??  = should it render a template??
9322             
9323             this.el.update(this.emptyText);
9324             return;
9325         }
9326         var el = this.el;
9327         if (this.dataName) {
9328             this.el.update(t.apply(this.store.meta)); //????
9329             el = this.el.child('.roo-tpl-' + this.dataName);
9330         }
9331         
9332         for(var i = 0, len = records.length; i < len; i++){
9333             var data = this.prepareData(records[i].data, i, records[i]);
9334             this.fireEvent("preparedata", this, data, i, records[i]);
9335             html[html.length] = Roo.util.Format.trim(
9336                 this.dataName ?
9337                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9338                     t.apply(data)
9339             );
9340         }
9341         
9342         
9343         
9344         el.update(html.join(""));
9345         this.nodes = el.dom.childNodes;
9346         this.updateIndexes(0);
9347     },
9348
9349     /**
9350      * Function to override to reformat the data that is sent to
9351      * the template for each node.
9352      * DEPRICATED - use the preparedata event handler.
9353      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9354      * a JSON object for an UpdateManager bound view).
9355      */
9356     prepareData : function(data, index, record)
9357     {
9358         this.fireEvent("preparedata", this, data, index, record);
9359         return data;
9360     },
9361
9362     onUpdate : function(ds, record){
9363         this.clearSelections();
9364         var index = this.store.indexOf(record);
9365         var n = this.nodes[index];
9366         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9367         n.parentNode.removeChild(n);
9368         this.updateIndexes(index, index);
9369     },
9370
9371     
9372     
9373 // --------- FIXME     
9374     onAdd : function(ds, records, index)
9375     {
9376         this.clearSelections();
9377         if(this.nodes.length == 0){
9378             this.refresh();
9379             return;
9380         }
9381         var n = this.nodes[index];
9382         for(var i = 0, len = records.length; i < len; i++){
9383             var d = this.prepareData(records[i].data, i, records[i]);
9384             if(n){
9385                 this.tpl.insertBefore(n, d);
9386             }else{
9387                 
9388                 this.tpl.append(this.el, d);
9389             }
9390         }
9391         this.updateIndexes(index);
9392     },
9393
9394     onRemove : function(ds, record, index){
9395         this.clearSelections();
9396         var el = this.dataName  ?
9397             this.el.child('.roo-tpl-' + this.dataName) :
9398             this.el; 
9399         el.dom.removeChild(this.nodes[index]);
9400         this.updateIndexes(index);
9401     },
9402
9403     /**
9404      * Refresh an individual node.
9405      * @param {Number} index
9406      */
9407     refreshNode : function(index){
9408         this.onUpdate(this.store, this.store.getAt(index));
9409     },
9410
9411     updateIndexes : function(startIndex, endIndex){
9412         var ns = this.nodes;
9413         startIndex = startIndex || 0;
9414         endIndex = endIndex || ns.length - 1;
9415         for(var i = startIndex; i <= endIndex; i++){
9416             ns[i].nodeIndex = i;
9417         }
9418     },
9419
9420     /**
9421      * Changes the data store this view uses and refresh the view.
9422      * @param {Store} store
9423      */
9424     setStore : function(store, initial){
9425         if(!initial && this.store){
9426             this.store.un("datachanged", this.refresh);
9427             this.store.un("add", this.onAdd);
9428             this.store.un("remove", this.onRemove);
9429             this.store.un("update", this.onUpdate);
9430             this.store.un("clear", this.refresh);
9431         }
9432         if(store){
9433           
9434             store.on("datachanged", this.refresh, this);
9435             store.on("add", this.onAdd, this);
9436             store.on("remove", this.onRemove, this);
9437             store.on("update", this.onUpdate, this);
9438             store.on("clear", this.refresh, this);
9439         }
9440         
9441         if(store){
9442             this.refresh();
9443         }
9444     },
9445
9446     /**
9447      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9448      * @param {HTMLElement} node
9449      * @return {HTMLElement} The template node
9450      */
9451     findItemFromChild : function(node){
9452         var el = this.dataName  ?
9453             this.el.child('.roo-tpl-' + this.dataName,true) :
9454             this.el.dom; 
9455         
9456         if(!node || node.parentNode == el){
9457                     return node;
9458             }
9459             var p = node.parentNode;
9460             while(p && p != el){
9461             if(p.parentNode == el){
9462                 return p;
9463             }
9464             p = p.parentNode;
9465         }
9466             return null;
9467     },
9468
9469     /** @ignore */
9470     onClick : function(e){
9471         var item = this.findItemFromChild(e.getTarget());
9472         if(item){
9473             var index = this.indexOf(item);
9474             if(this.onItemClick(item, index, e) !== false){
9475                 this.fireEvent("click", this, index, item, e);
9476             }
9477         }else{
9478             this.clearSelections();
9479         }
9480     },
9481
9482     /** @ignore */
9483     onContextMenu : function(e){
9484         var item = this.findItemFromChild(e.getTarget());
9485         if(item){
9486             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9487         }
9488     },
9489
9490     /** @ignore */
9491     onDblClick : function(e){
9492         var item = this.findItemFromChild(e.getTarget());
9493         if(item){
9494             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9495         }
9496     },
9497
9498     onItemClick : function(item, index, e)
9499     {
9500         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9501             return false;
9502         }
9503         if (this.toggleSelect) {
9504             var m = this.isSelected(item) ? 'unselect' : 'select';
9505             Roo.log(m);
9506             var _t = this;
9507             _t[m](item, true, false);
9508             return true;
9509         }
9510         if(this.multiSelect || this.singleSelect){
9511             if(this.multiSelect && e.shiftKey && this.lastSelection){
9512                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9513             }else{
9514                 this.select(item, this.multiSelect && e.ctrlKey);
9515                 this.lastSelection = item;
9516             }
9517             e.preventDefault();
9518         }
9519         return true;
9520     },
9521
9522     /**
9523      * Get the number of selected nodes.
9524      * @return {Number}
9525      */
9526     getSelectionCount : function(){
9527         return this.selections.length;
9528     },
9529
9530     /**
9531      * Get the currently selected nodes.
9532      * @return {Array} An array of HTMLElements
9533      */
9534     getSelectedNodes : function(){
9535         return this.selections;
9536     },
9537
9538     /**
9539      * Get the indexes of the selected nodes.
9540      * @return {Array}
9541      */
9542     getSelectedIndexes : function(){
9543         var indexes = [], s = this.selections;
9544         for(var i = 0, len = s.length; i < len; i++){
9545             indexes.push(s[i].nodeIndex);
9546         }
9547         return indexes;
9548     },
9549
9550     /**
9551      * Clear all selections
9552      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9553      */
9554     clearSelections : function(suppressEvent){
9555         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9556             this.cmp.elements = this.selections;
9557             this.cmp.removeClass(this.selectedClass);
9558             this.selections = [];
9559             if(!suppressEvent){
9560                 this.fireEvent("selectionchange", this, this.selections);
9561             }
9562         }
9563     },
9564
9565     /**
9566      * Returns true if the passed node is selected
9567      * @param {HTMLElement/Number} node The node or node index
9568      * @return {Boolean}
9569      */
9570     isSelected : function(node){
9571         var s = this.selections;
9572         if(s.length < 1){
9573             return false;
9574         }
9575         node = this.getNode(node);
9576         return s.indexOf(node) !== -1;
9577     },
9578
9579     /**
9580      * Selects nodes.
9581      * @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
9582      * @param {Boolean} keepExisting (optional) true to keep existing selections
9583      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9584      */
9585     select : function(nodeInfo, keepExisting, suppressEvent){
9586         if(nodeInfo instanceof Array){
9587             if(!keepExisting){
9588                 this.clearSelections(true);
9589             }
9590             for(var i = 0, len = nodeInfo.length; i < len; i++){
9591                 this.select(nodeInfo[i], true, true);
9592             }
9593             return;
9594         } 
9595         var node = this.getNode(nodeInfo);
9596         if(!node || this.isSelected(node)){
9597             return; // already selected.
9598         }
9599         if(!keepExisting){
9600             this.clearSelections(true);
9601         }
9602         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9603             Roo.fly(node).addClass(this.selectedClass);
9604             this.selections.push(node);
9605             if(!suppressEvent){
9606                 this.fireEvent("selectionchange", this, this.selections);
9607             }
9608         }
9609         
9610         
9611     },
9612       /**
9613      * Unselects nodes.
9614      * @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
9615      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9616      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9617      */
9618     unselect : function(nodeInfo, keepExisting, suppressEvent)
9619     {
9620         if(nodeInfo instanceof Array){
9621             Roo.each(this.selections, function(s) {
9622                 this.unselect(s, nodeInfo);
9623             }, this);
9624             return;
9625         }
9626         var node = this.getNode(nodeInfo);
9627         if(!node || !this.isSelected(node)){
9628             Roo.log("not selected");
9629             return; // not selected.
9630         }
9631         // fireevent???
9632         var ns = [];
9633         Roo.each(this.selections, function(s) {
9634             if (s == node ) {
9635                 Roo.fly(node).removeClass(this.selectedClass);
9636
9637                 return;
9638             }
9639             ns.push(s);
9640         },this);
9641         
9642         this.selections= ns;
9643         this.fireEvent("selectionchange", this, this.selections);
9644     },
9645
9646     /**
9647      * Gets a template node.
9648      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9649      * @return {HTMLElement} The node or null if it wasn't found
9650      */
9651     getNode : function(nodeInfo){
9652         if(typeof nodeInfo == "string"){
9653             return document.getElementById(nodeInfo);
9654         }else if(typeof nodeInfo == "number"){
9655             return this.nodes[nodeInfo];
9656         }
9657         return nodeInfo;
9658     },
9659
9660     /**
9661      * Gets a range template nodes.
9662      * @param {Number} startIndex
9663      * @param {Number} endIndex
9664      * @return {Array} An array of nodes
9665      */
9666     getNodes : function(start, end){
9667         var ns = this.nodes;
9668         start = start || 0;
9669         end = typeof end == "undefined" ? ns.length - 1 : end;
9670         var nodes = [];
9671         if(start <= end){
9672             for(var i = start; i <= end; i++){
9673                 nodes.push(ns[i]);
9674             }
9675         } else{
9676             for(var i = start; i >= end; i--){
9677                 nodes.push(ns[i]);
9678             }
9679         }
9680         return nodes;
9681     },
9682
9683     /**
9684      * Finds the index of the passed node
9685      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9686      * @return {Number} The index of the node or -1
9687      */
9688     indexOf : function(node){
9689         node = this.getNode(node);
9690         if(typeof node.nodeIndex == "number"){
9691             return node.nodeIndex;
9692         }
9693         var ns = this.nodes;
9694         for(var i = 0, len = ns.length; i < len; i++){
9695             if(ns[i] == node){
9696                 return i;
9697             }
9698         }
9699         return -1;
9700     }
9701 });
9702 /*
9703  * Based on:
9704  * Ext JS Library 1.1.1
9705  * Copyright(c) 2006-2007, Ext JS, LLC.
9706  *
9707  * Originally Released Under LGPL - original licence link has changed is not relivant.
9708  *
9709  * Fork - LGPL
9710  * <script type="text/javascript">
9711  */
9712
9713 /**
9714  * @class Roo.JsonView
9715  * @extends Roo.View
9716  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9717 <pre><code>
9718 var view = new Roo.JsonView({
9719     container: "my-element",
9720     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9721     multiSelect: true, 
9722     jsonRoot: "data" 
9723 });
9724
9725 // listen for node click?
9726 view.on("click", function(vw, index, node, e){
9727     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9728 });
9729
9730 // direct load of JSON data
9731 view.load("foobar.php");
9732
9733 // Example from my blog list
9734 var tpl = new Roo.Template(
9735     '&lt;div class="entry"&gt;' +
9736     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9737     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9738     "&lt;/div&gt;&lt;hr /&gt;"
9739 );
9740
9741 var moreView = new Roo.JsonView({
9742     container :  "entry-list", 
9743     template : tpl,
9744     jsonRoot: "posts"
9745 });
9746 moreView.on("beforerender", this.sortEntries, this);
9747 moreView.load({
9748     url: "/blog/get-posts.php",
9749     params: "allposts=true",
9750     text: "Loading Blog Entries..."
9751 });
9752 </code></pre>
9753
9754 * Note: old code is supported with arguments : (container, template, config)
9755
9756
9757  * @constructor
9758  * Create a new JsonView
9759  * 
9760  * @param {Object} config The config object
9761  * 
9762  */
9763 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9764     
9765     
9766     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9767
9768     var um = this.el.getUpdateManager();
9769     um.setRenderer(this);
9770     um.on("update", this.onLoad, this);
9771     um.on("failure", this.onLoadException, this);
9772
9773     /**
9774      * @event beforerender
9775      * Fires before rendering of the downloaded JSON data.
9776      * @param {Roo.JsonView} this
9777      * @param {Object} data The JSON data loaded
9778      */
9779     /**
9780      * @event load
9781      * Fires when data is loaded.
9782      * @param {Roo.JsonView} this
9783      * @param {Object} data The JSON data loaded
9784      * @param {Object} response The raw Connect response object
9785      */
9786     /**
9787      * @event loadexception
9788      * Fires when loading fails.
9789      * @param {Roo.JsonView} this
9790      * @param {Object} response The raw Connect response object
9791      */
9792     this.addEvents({
9793         'beforerender' : true,
9794         'load' : true,
9795         'loadexception' : true
9796     });
9797 };
9798 Roo.extend(Roo.JsonView, Roo.View, {
9799     /**
9800      * @type {String} The root property in the loaded JSON object that contains the data
9801      */
9802     jsonRoot : "",
9803
9804     /**
9805      * Refreshes the view.
9806      */
9807     refresh : function(){
9808         this.clearSelections();
9809         this.el.update("");
9810         var html = [];
9811         var o = this.jsonData;
9812         if(o && o.length > 0){
9813             for(var i = 0, len = o.length; i < len; i++){
9814                 var data = this.prepareData(o[i], i, o);
9815                 html[html.length] = this.tpl.apply(data);
9816             }
9817         }else{
9818             html.push(this.emptyText);
9819         }
9820         this.el.update(html.join(""));
9821         this.nodes = this.el.dom.childNodes;
9822         this.updateIndexes(0);
9823     },
9824
9825     /**
9826      * 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.
9827      * @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:
9828      <pre><code>
9829      view.load({
9830          url: "your-url.php",
9831          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9832          callback: yourFunction,
9833          scope: yourObject, //(optional scope)
9834          discardUrl: false,
9835          nocache: false,
9836          text: "Loading...",
9837          timeout: 30,
9838          scripts: false
9839      });
9840      </code></pre>
9841      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9842      * 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.
9843      * @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}
9844      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9845      * @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.
9846      */
9847     load : function(){
9848         var um = this.el.getUpdateManager();
9849         um.update.apply(um, arguments);
9850     },
9851
9852     render : function(el, response){
9853         this.clearSelections();
9854         this.el.update("");
9855         var o;
9856         try{
9857             o = Roo.util.JSON.decode(response.responseText);
9858             if(this.jsonRoot){
9859                 
9860                 o = o[this.jsonRoot];
9861             }
9862         } catch(e){
9863         }
9864         /**
9865          * The current JSON data or null
9866          */
9867         this.jsonData = o;
9868         this.beforeRender();
9869         this.refresh();
9870     },
9871
9872 /**
9873  * Get the number of records in the current JSON dataset
9874  * @return {Number}
9875  */
9876     getCount : function(){
9877         return this.jsonData ? this.jsonData.length : 0;
9878     },
9879
9880 /**
9881  * Returns the JSON object for the specified node(s)
9882  * @param {HTMLElement/Array} node The node or an array of nodes
9883  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9884  * you get the JSON object for the node
9885  */
9886     getNodeData : function(node){
9887         if(node instanceof Array){
9888             var data = [];
9889             for(var i = 0, len = node.length; i < len; i++){
9890                 data.push(this.getNodeData(node[i]));
9891             }
9892             return data;
9893         }
9894         return this.jsonData[this.indexOf(node)] || null;
9895     },
9896
9897     beforeRender : function(){
9898         this.snapshot = this.jsonData;
9899         if(this.sortInfo){
9900             this.sort.apply(this, this.sortInfo);
9901         }
9902         this.fireEvent("beforerender", this, this.jsonData);
9903     },
9904
9905     onLoad : function(el, o){
9906         this.fireEvent("load", this, this.jsonData, o);
9907     },
9908
9909     onLoadException : function(el, o){
9910         this.fireEvent("loadexception", this, o);
9911     },
9912
9913 /**
9914  * Filter the data by a specific property.
9915  * @param {String} property A property on your JSON objects
9916  * @param {String/RegExp} value Either string that the property values
9917  * should start with, or a RegExp to test against the property
9918  */
9919     filter : function(property, value){
9920         if(this.jsonData){
9921             var data = [];
9922             var ss = this.snapshot;
9923             if(typeof value == "string"){
9924                 var vlen = value.length;
9925                 if(vlen == 0){
9926                     this.clearFilter();
9927                     return;
9928                 }
9929                 value = value.toLowerCase();
9930                 for(var i = 0, len = ss.length; i < len; i++){
9931                     var o = ss[i];
9932                     if(o[property].substr(0, vlen).toLowerCase() == value){
9933                         data.push(o);
9934                     }
9935                 }
9936             } else if(value.exec){ // regex?
9937                 for(var i = 0, len = ss.length; i < len; i++){
9938                     var o = ss[i];
9939                     if(value.test(o[property])){
9940                         data.push(o);
9941                     }
9942                 }
9943             } else{
9944                 return;
9945             }
9946             this.jsonData = data;
9947             this.refresh();
9948         }
9949     },
9950
9951 /**
9952  * Filter by a function. The passed function will be called with each
9953  * object in the current dataset. If the function returns true the value is kept,
9954  * otherwise it is filtered.
9955  * @param {Function} fn
9956  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9957  */
9958     filterBy : function(fn, scope){
9959         if(this.jsonData){
9960             var data = [];
9961             var ss = this.snapshot;
9962             for(var i = 0, len = ss.length; i < len; i++){
9963                 var o = ss[i];
9964                 if(fn.call(scope || this, o)){
9965                     data.push(o);
9966                 }
9967             }
9968             this.jsonData = data;
9969             this.refresh();
9970         }
9971     },
9972
9973 /**
9974  * Clears the current filter.
9975  */
9976     clearFilter : function(){
9977         if(this.snapshot && this.jsonData != this.snapshot){
9978             this.jsonData = this.snapshot;
9979             this.refresh();
9980         }
9981     },
9982
9983
9984 /**
9985  * Sorts the data for this view and refreshes it.
9986  * @param {String} property A property on your JSON objects to sort on
9987  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9988  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9989  */
9990     sort : function(property, dir, sortType){
9991         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9992         if(this.jsonData){
9993             var p = property;
9994             var dsc = dir && dir.toLowerCase() == "desc";
9995             var f = function(o1, o2){
9996                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9997                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9998                 ;
9999                 if(v1 < v2){
10000                     return dsc ? +1 : -1;
10001                 } else if(v1 > v2){
10002                     return dsc ? -1 : +1;
10003                 } else{
10004                     return 0;
10005                 }
10006             };
10007             this.jsonData.sort(f);
10008             this.refresh();
10009             if(this.jsonData != this.snapshot){
10010                 this.snapshot.sort(f);
10011             }
10012         }
10013     }
10014 });/*
10015  * Based on:
10016  * Ext JS Library 1.1.1
10017  * Copyright(c) 2006-2007, Ext JS, LLC.
10018  *
10019  * Originally Released Under LGPL - original licence link has changed is not relivant.
10020  *
10021  * Fork - LGPL
10022  * <script type="text/javascript">
10023  */
10024  
10025
10026 /**
10027  * @class Roo.ColorPalette
10028  * @extends Roo.Component
10029  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10030  * Here's an example of typical usage:
10031  * <pre><code>
10032 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10033 cp.render('my-div');
10034
10035 cp.on('select', function(palette, selColor){
10036     // do something with selColor
10037 });
10038 </code></pre>
10039  * @constructor
10040  * Create a new ColorPalette
10041  * @param {Object} config The config object
10042  */
10043 Roo.ColorPalette = function(config){
10044     Roo.ColorPalette.superclass.constructor.call(this, config);
10045     this.addEvents({
10046         /**
10047              * @event select
10048              * Fires when a color is selected
10049              * @param {ColorPalette} this
10050              * @param {String} color The 6-digit color hex code (without the # symbol)
10051              */
10052         select: true
10053     });
10054
10055     if(this.handler){
10056         this.on("select", this.handler, this.scope, true);
10057     }
10058 };
10059 Roo.extend(Roo.ColorPalette, Roo.Component, {
10060     /**
10061      * @cfg {String} itemCls
10062      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10063      */
10064     itemCls : "x-color-palette",
10065     /**
10066      * @cfg {String} value
10067      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10068      * the hex codes are case-sensitive.
10069      */
10070     value : null,
10071     clickEvent:'click',
10072     // private
10073     ctype: "Roo.ColorPalette",
10074
10075     /**
10076      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10077      */
10078     allowReselect : false,
10079
10080     /**
10081      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10082      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10083      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10084      * of colors with the width setting until the box is symmetrical.</p>
10085      * <p>You can override individual colors if needed:</p>
10086      * <pre><code>
10087 var cp = new Roo.ColorPalette();
10088 cp.colors[0] = "FF0000";  // change the first box to red
10089 </code></pre>
10090
10091 Or you can provide a custom array of your own for complete control:
10092 <pre><code>
10093 var cp = new Roo.ColorPalette();
10094 cp.colors = ["000000", "993300", "333300"];
10095 </code></pre>
10096      * @type Array
10097      */
10098     colors : [
10099         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10100         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10101         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10102         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10103         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10104     ],
10105
10106     // private
10107     onRender : function(container, position){
10108         var t = new Roo.MasterTemplate(
10109             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10110         );
10111         var c = this.colors;
10112         for(var i = 0, len = c.length; i < len; i++){
10113             t.add([c[i]]);
10114         }
10115         var el = document.createElement("div");
10116         el.className = this.itemCls;
10117         t.overwrite(el);
10118         container.dom.insertBefore(el, position);
10119         this.el = Roo.get(el);
10120         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10121         if(this.clickEvent != 'click'){
10122             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10123         }
10124     },
10125
10126     // private
10127     afterRender : function(){
10128         Roo.ColorPalette.superclass.afterRender.call(this);
10129         if(this.value){
10130             var s = this.value;
10131             this.value = null;
10132             this.select(s);
10133         }
10134     },
10135
10136     // private
10137     handleClick : function(e, t){
10138         e.preventDefault();
10139         if(!this.disabled){
10140             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10141             this.select(c.toUpperCase());
10142         }
10143     },
10144
10145     /**
10146      * Selects the specified color in the palette (fires the select event)
10147      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10148      */
10149     select : function(color){
10150         color = color.replace("#", "");
10151         if(color != this.value || this.allowReselect){
10152             var el = this.el;
10153             if(this.value){
10154                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10155             }
10156             el.child("a.color-"+color).addClass("x-color-palette-sel");
10157             this.value = color;
10158             this.fireEvent("select", this, color);
10159         }
10160     }
10161 });/*
10162  * Based on:
10163  * Ext JS Library 1.1.1
10164  * Copyright(c) 2006-2007, Ext JS, LLC.
10165  *
10166  * Originally Released Under LGPL - original licence link has changed is not relivant.
10167  *
10168  * Fork - LGPL
10169  * <script type="text/javascript">
10170  */
10171  
10172 /**
10173  * @class Roo.DatePicker
10174  * @extends Roo.Component
10175  * Simple date picker class.
10176  * @constructor
10177  * Create a new DatePicker
10178  * @param {Object} config The config object
10179  */
10180 Roo.DatePicker = function(config){
10181     Roo.DatePicker.superclass.constructor.call(this, config);
10182
10183     this.value = config && config.value ?
10184                  config.value.clearTime() : new Date().clearTime();
10185
10186     this.addEvents({
10187         /**
10188              * @event select
10189              * Fires when a date is selected
10190              * @param {DatePicker} this
10191              * @param {Date} date The selected date
10192              */
10193         'select': true,
10194         /**
10195              * @event monthchange
10196              * Fires when the displayed month changes 
10197              * @param {DatePicker} this
10198              * @param {Date} date The selected month
10199              */
10200         'monthchange': true
10201     });
10202
10203     if(this.handler){
10204         this.on("select", this.handler,  this.scope || this);
10205     }
10206     // build the disabledDatesRE
10207     if(!this.disabledDatesRE && this.disabledDates){
10208         var dd = this.disabledDates;
10209         var re = "(?:";
10210         for(var i = 0; i < dd.length; i++){
10211             re += dd[i];
10212             if(i != dd.length-1) re += "|";
10213         }
10214         this.disabledDatesRE = new RegExp(re + ")");
10215     }
10216 };
10217
10218 Roo.extend(Roo.DatePicker, Roo.Component, {
10219     /**
10220      * @cfg {String} todayText
10221      * The text to display on the button that selects the current date (defaults to "Today")
10222      */
10223     todayText : "Today",
10224     /**
10225      * @cfg {String} okText
10226      * The text to display on the ok button
10227      */
10228     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10229     /**
10230      * @cfg {String} cancelText
10231      * The text to display on the cancel button
10232      */
10233     cancelText : "Cancel",
10234     /**
10235      * @cfg {String} todayTip
10236      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10237      */
10238     todayTip : "{0} (Spacebar)",
10239     /**
10240      * @cfg {Date} minDate
10241      * Minimum allowable date (JavaScript date object, defaults to null)
10242      */
10243     minDate : null,
10244     /**
10245      * @cfg {Date} maxDate
10246      * Maximum allowable date (JavaScript date object, defaults to null)
10247      */
10248     maxDate : null,
10249     /**
10250      * @cfg {String} minText
10251      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10252      */
10253     minText : "This date is before the minimum date",
10254     /**
10255      * @cfg {String} maxText
10256      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10257      */
10258     maxText : "This date is after the maximum date",
10259     /**
10260      * @cfg {String} format
10261      * The default date format string which can be overriden for localization support.  The format must be
10262      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10263      */
10264     format : "m/d/y",
10265     /**
10266      * @cfg {Array} disabledDays
10267      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10268      */
10269     disabledDays : null,
10270     /**
10271      * @cfg {String} disabledDaysText
10272      * The tooltip to display when the date falls on a disabled day (defaults to "")
10273      */
10274     disabledDaysText : "",
10275     /**
10276      * @cfg {RegExp} disabledDatesRE
10277      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10278      */
10279     disabledDatesRE : null,
10280     /**
10281      * @cfg {String} disabledDatesText
10282      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10283      */
10284     disabledDatesText : "",
10285     /**
10286      * @cfg {Boolean} constrainToViewport
10287      * True to constrain the date picker to the viewport (defaults to true)
10288      */
10289     constrainToViewport : true,
10290     /**
10291      * @cfg {Array} monthNames
10292      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10293      */
10294     monthNames : Date.monthNames,
10295     /**
10296      * @cfg {Array} dayNames
10297      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10298      */
10299     dayNames : Date.dayNames,
10300     /**
10301      * @cfg {String} nextText
10302      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10303      */
10304     nextText: 'Next Month (Control+Right)',
10305     /**
10306      * @cfg {String} prevText
10307      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10308      */
10309     prevText: 'Previous Month (Control+Left)',
10310     /**
10311      * @cfg {String} monthYearText
10312      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10313      */
10314     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10315     /**
10316      * @cfg {Number} startDay
10317      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10318      */
10319     startDay : 0,
10320     /**
10321      * @cfg {Bool} showClear
10322      * Show a clear button (usefull for date form elements that can be blank.)
10323      */
10324     
10325     showClear: false,
10326     
10327     /**
10328      * Sets the value of the date field
10329      * @param {Date} value The date to set
10330      */
10331     setValue : function(value){
10332         var old = this.value;
10333         this.value = value.clearTime(true);
10334         if(this.el){
10335             this.update(this.value);
10336         }
10337     },
10338
10339     /**
10340      * Gets the current selected value of the date field
10341      * @return {Date} The selected date
10342      */
10343     getValue : function(){
10344         return this.value;
10345     },
10346
10347     // private
10348     focus : function(){
10349         if(this.el){
10350             this.update(this.activeDate);
10351         }
10352     },
10353
10354     // private
10355     onRender : function(container, position){
10356         var m = [
10357              '<table cellspacing="0">',
10358                 '<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>',
10359                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10360         var dn = this.dayNames;
10361         for(var i = 0; i < 7; i++){
10362             var d = this.startDay+i;
10363             if(d > 6){
10364                 d = d-7;
10365             }
10366             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10367         }
10368         m[m.length] = "</tr></thead><tbody><tr>";
10369         for(var i = 0; i < 42; i++) {
10370             if(i % 7 == 0 && i != 0){
10371                 m[m.length] = "</tr><tr>";
10372             }
10373             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10374         }
10375         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10376             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10377
10378         var el = document.createElement("div");
10379         el.className = "x-date-picker";
10380         el.innerHTML = m.join("");
10381
10382         container.dom.insertBefore(el, position);
10383
10384         this.el = Roo.get(el);
10385         this.eventEl = Roo.get(el.firstChild);
10386
10387         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10388             handler: this.showPrevMonth,
10389             scope: this,
10390             preventDefault:true,
10391             stopDefault:true
10392         });
10393
10394         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10395             handler: this.showNextMonth,
10396             scope: this,
10397             preventDefault:true,
10398             stopDefault:true
10399         });
10400
10401         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10402
10403         this.monthPicker = this.el.down('div.x-date-mp');
10404         this.monthPicker.enableDisplayMode('block');
10405         
10406         var kn = new Roo.KeyNav(this.eventEl, {
10407             "left" : function(e){
10408                 e.ctrlKey ?
10409                     this.showPrevMonth() :
10410                     this.update(this.activeDate.add("d", -1));
10411             },
10412
10413             "right" : function(e){
10414                 e.ctrlKey ?
10415                     this.showNextMonth() :
10416                     this.update(this.activeDate.add("d", 1));
10417             },
10418
10419             "up" : function(e){
10420                 e.ctrlKey ?
10421                     this.showNextYear() :
10422                     this.update(this.activeDate.add("d", -7));
10423             },
10424
10425             "down" : function(e){
10426                 e.ctrlKey ?
10427                     this.showPrevYear() :
10428                     this.update(this.activeDate.add("d", 7));
10429             },
10430
10431             "pageUp" : function(e){
10432                 this.showNextMonth();
10433             },
10434
10435             "pageDown" : function(e){
10436                 this.showPrevMonth();
10437             },
10438
10439             "enter" : function(e){
10440                 e.stopPropagation();
10441                 return true;
10442             },
10443
10444             scope : this
10445         });
10446
10447         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10448
10449         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10450
10451         this.el.unselectable();
10452         
10453         this.cells = this.el.select("table.x-date-inner tbody td");
10454         this.textNodes = this.el.query("table.x-date-inner tbody span");
10455
10456         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10457             text: "&#160;",
10458             tooltip: this.monthYearText
10459         });
10460
10461         this.mbtn.on('click', this.showMonthPicker, this);
10462         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10463
10464
10465         var today = (new Date()).dateFormat(this.format);
10466         
10467         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10468         if (this.showClear) {
10469             baseTb.add( new Roo.Toolbar.Fill());
10470         }
10471         baseTb.add({
10472             text: String.format(this.todayText, today),
10473             tooltip: String.format(this.todayTip, today),
10474             handler: this.selectToday,
10475             scope: this
10476         });
10477         
10478         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10479             
10480         //});
10481         if (this.showClear) {
10482             
10483             baseTb.add( new Roo.Toolbar.Fill());
10484             baseTb.add({
10485                 text: '&#160;',
10486                 cls: 'x-btn-icon x-btn-clear',
10487                 handler: function() {
10488                     //this.value = '';
10489                     this.fireEvent("select", this, '');
10490                 },
10491                 scope: this
10492             });
10493         }
10494         
10495         
10496         if(Roo.isIE){
10497             this.el.repaint();
10498         }
10499         this.update(this.value);
10500     },
10501
10502     createMonthPicker : function(){
10503         if(!this.monthPicker.dom.firstChild){
10504             var buf = ['<table border="0" cellspacing="0">'];
10505             for(var i = 0; i < 6; i++){
10506                 buf.push(
10507                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10508                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10509                     i == 0 ?
10510                     '<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>' :
10511                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10512                 );
10513             }
10514             buf.push(
10515                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10516                     this.okText,
10517                     '</button><button type="button" class="x-date-mp-cancel">',
10518                     this.cancelText,
10519                     '</button></td></tr>',
10520                 '</table>'
10521             );
10522             this.monthPicker.update(buf.join(''));
10523             this.monthPicker.on('click', this.onMonthClick, this);
10524             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10525
10526             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10527             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10528
10529             this.mpMonths.each(function(m, a, i){
10530                 i += 1;
10531                 if((i%2) == 0){
10532                     m.dom.xmonth = 5 + Math.round(i * .5);
10533                 }else{
10534                     m.dom.xmonth = Math.round((i-1) * .5);
10535                 }
10536             });
10537         }
10538     },
10539
10540     showMonthPicker : function(){
10541         this.createMonthPicker();
10542         var size = this.el.getSize();
10543         this.monthPicker.setSize(size);
10544         this.monthPicker.child('table').setSize(size);
10545
10546         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10547         this.updateMPMonth(this.mpSelMonth);
10548         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10549         this.updateMPYear(this.mpSelYear);
10550
10551         this.monthPicker.slideIn('t', {duration:.2});
10552     },
10553
10554     updateMPYear : function(y){
10555         this.mpyear = y;
10556         var ys = this.mpYears.elements;
10557         for(var i = 1; i <= 10; i++){
10558             var td = ys[i-1], y2;
10559             if((i%2) == 0){
10560                 y2 = y + Math.round(i * .5);
10561                 td.firstChild.innerHTML = y2;
10562                 td.xyear = y2;
10563             }else{
10564                 y2 = y - (5-Math.round(i * .5));
10565                 td.firstChild.innerHTML = y2;
10566                 td.xyear = y2;
10567             }
10568             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10569         }
10570     },
10571
10572     updateMPMonth : function(sm){
10573         this.mpMonths.each(function(m, a, i){
10574             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10575         });
10576     },
10577
10578     selectMPMonth: function(m){
10579         
10580     },
10581
10582     onMonthClick : function(e, t){
10583         e.stopEvent();
10584         var el = new Roo.Element(t), pn;
10585         if(el.is('button.x-date-mp-cancel')){
10586             this.hideMonthPicker();
10587         }
10588         else if(el.is('button.x-date-mp-ok')){
10589             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10590             this.hideMonthPicker();
10591         }
10592         else if(pn = el.up('td.x-date-mp-month', 2)){
10593             this.mpMonths.removeClass('x-date-mp-sel');
10594             pn.addClass('x-date-mp-sel');
10595             this.mpSelMonth = pn.dom.xmonth;
10596         }
10597         else if(pn = el.up('td.x-date-mp-year', 2)){
10598             this.mpYears.removeClass('x-date-mp-sel');
10599             pn.addClass('x-date-mp-sel');
10600             this.mpSelYear = pn.dom.xyear;
10601         }
10602         else if(el.is('a.x-date-mp-prev')){
10603             this.updateMPYear(this.mpyear-10);
10604         }
10605         else if(el.is('a.x-date-mp-next')){
10606             this.updateMPYear(this.mpyear+10);
10607         }
10608     },
10609
10610     onMonthDblClick : function(e, t){
10611         e.stopEvent();
10612         var el = new Roo.Element(t), pn;
10613         if(pn = el.up('td.x-date-mp-month', 2)){
10614             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10615             this.hideMonthPicker();
10616         }
10617         else if(pn = el.up('td.x-date-mp-year', 2)){
10618             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10619             this.hideMonthPicker();
10620         }
10621     },
10622
10623     hideMonthPicker : function(disableAnim){
10624         if(this.monthPicker){
10625             if(disableAnim === true){
10626                 this.monthPicker.hide();
10627             }else{
10628                 this.monthPicker.slideOut('t', {duration:.2});
10629             }
10630         }
10631     },
10632
10633     // private
10634     showPrevMonth : function(e){
10635         this.update(this.activeDate.add("mo", -1));
10636     },
10637
10638     // private
10639     showNextMonth : function(e){
10640         this.update(this.activeDate.add("mo", 1));
10641     },
10642
10643     // private
10644     showPrevYear : function(){
10645         this.update(this.activeDate.add("y", -1));
10646     },
10647
10648     // private
10649     showNextYear : function(){
10650         this.update(this.activeDate.add("y", 1));
10651     },
10652
10653     // private
10654     handleMouseWheel : function(e){
10655         var delta = e.getWheelDelta();
10656         if(delta > 0){
10657             this.showPrevMonth();
10658             e.stopEvent();
10659         } else if(delta < 0){
10660             this.showNextMonth();
10661             e.stopEvent();
10662         }
10663     },
10664
10665     // private
10666     handleDateClick : function(e, t){
10667         e.stopEvent();
10668         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10669             this.setValue(new Date(t.dateValue));
10670             this.fireEvent("select", this, this.value);
10671         }
10672     },
10673
10674     // private
10675     selectToday : function(){
10676         this.setValue(new Date().clearTime());
10677         this.fireEvent("select", this, this.value);
10678     },
10679
10680     // private
10681     update : function(date)
10682     {
10683         var vd = this.activeDate;
10684         this.activeDate = date;
10685         if(vd && this.el){
10686             var t = date.getTime();
10687             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10688                 this.cells.removeClass("x-date-selected");
10689                 this.cells.each(function(c){
10690                    if(c.dom.firstChild.dateValue == t){
10691                        c.addClass("x-date-selected");
10692                        setTimeout(function(){
10693                             try{c.dom.firstChild.focus();}catch(e){}
10694                        }, 50);
10695                        return false;
10696                    }
10697                 });
10698                 return;
10699             }
10700         }
10701         
10702         var days = date.getDaysInMonth();
10703         var firstOfMonth = date.getFirstDateOfMonth();
10704         var startingPos = firstOfMonth.getDay()-this.startDay;
10705
10706         if(startingPos <= this.startDay){
10707             startingPos += 7;
10708         }
10709
10710         var pm = date.add("mo", -1);
10711         var prevStart = pm.getDaysInMonth()-startingPos;
10712
10713         var cells = this.cells.elements;
10714         var textEls = this.textNodes;
10715         days += startingPos;
10716
10717         // convert everything to numbers so it's fast
10718         var day = 86400000;
10719         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10720         var today = new Date().clearTime().getTime();
10721         var sel = date.clearTime().getTime();
10722         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10723         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10724         var ddMatch = this.disabledDatesRE;
10725         var ddText = this.disabledDatesText;
10726         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10727         var ddaysText = this.disabledDaysText;
10728         var format = this.format;
10729
10730         var setCellClass = function(cal, cell){
10731             cell.title = "";
10732             var t = d.getTime();
10733             cell.firstChild.dateValue = t;
10734             if(t == today){
10735                 cell.className += " x-date-today";
10736                 cell.title = cal.todayText;
10737             }
10738             if(t == sel){
10739                 cell.className += " x-date-selected";
10740                 setTimeout(function(){
10741                     try{cell.firstChild.focus();}catch(e){}
10742                 }, 50);
10743             }
10744             // disabling
10745             if(t < min) {
10746                 cell.className = " x-date-disabled";
10747                 cell.title = cal.minText;
10748                 return;
10749             }
10750             if(t > max) {
10751                 cell.className = " x-date-disabled";
10752                 cell.title = cal.maxText;
10753                 return;
10754             }
10755             if(ddays){
10756                 if(ddays.indexOf(d.getDay()) != -1){
10757                     cell.title = ddaysText;
10758                     cell.className = " x-date-disabled";
10759                 }
10760             }
10761             if(ddMatch && format){
10762                 var fvalue = d.dateFormat(format);
10763                 if(ddMatch.test(fvalue)){
10764                     cell.title = ddText.replace("%0", fvalue);
10765                     cell.className = " x-date-disabled";
10766                 }
10767             }
10768         };
10769
10770         var i = 0;
10771         for(; i < startingPos; i++) {
10772             textEls[i].innerHTML = (++prevStart);
10773             d.setDate(d.getDate()+1);
10774             cells[i].className = "x-date-prevday";
10775             setCellClass(this, cells[i]);
10776         }
10777         for(; i < days; i++){
10778             intDay = i - startingPos + 1;
10779             textEls[i].innerHTML = (intDay);
10780             d.setDate(d.getDate()+1);
10781             cells[i].className = "x-date-active";
10782             setCellClass(this, cells[i]);
10783         }
10784         var extraDays = 0;
10785         for(; i < 42; i++) {
10786              textEls[i].innerHTML = (++extraDays);
10787              d.setDate(d.getDate()+1);
10788              cells[i].className = "x-date-nextday";
10789              setCellClass(this, cells[i]);
10790         }
10791
10792         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10793         this.fireEvent('monthchange', this, date);
10794         
10795         if(!this.internalRender){
10796             var main = this.el.dom.firstChild;
10797             var w = main.offsetWidth;
10798             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10799             Roo.fly(main).setWidth(w);
10800             this.internalRender = true;
10801             // opera does not respect the auto grow header center column
10802             // then, after it gets a width opera refuses to recalculate
10803             // without a second pass
10804             if(Roo.isOpera && !this.secondPass){
10805                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10806                 this.secondPass = true;
10807                 this.update.defer(10, this, [date]);
10808             }
10809         }
10810         
10811         
10812     }
10813 });        /*
10814  * Based on:
10815  * Ext JS Library 1.1.1
10816  * Copyright(c) 2006-2007, Ext JS, LLC.
10817  *
10818  * Originally Released Under LGPL - original licence link has changed is not relivant.
10819  *
10820  * Fork - LGPL
10821  * <script type="text/javascript">
10822  */
10823 /**
10824  * @class Roo.TabPanel
10825  * @extends Roo.util.Observable
10826  * A lightweight tab container.
10827  * <br><br>
10828  * Usage:
10829  * <pre><code>
10830 // basic tabs 1, built from existing content
10831 var tabs = new Roo.TabPanel("tabs1");
10832 tabs.addTab("script", "View Script");
10833 tabs.addTab("markup", "View Markup");
10834 tabs.activate("script");
10835
10836 // more advanced tabs, built from javascript
10837 var jtabs = new Roo.TabPanel("jtabs");
10838 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10839
10840 // set up the UpdateManager
10841 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10842 var updater = tab2.getUpdateManager();
10843 updater.setDefaultUrl("ajax1.htm");
10844 tab2.on('activate', updater.refresh, updater, true);
10845
10846 // Use setUrl for Ajax loading
10847 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10848 tab3.setUrl("ajax2.htm", null, true);
10849
10850 // Disabled tab
10851 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10852 tab4.disable();
10853
10854 jtabs.activate("jtabs-1");
10855  * </code></pre>
10856  * @constructor
10857  * Create a new TabPanel.
10858  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10859  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10860  */
10861 Roo.TabPanel = function(container, config){
10862     /**
10863     * The container element for this TabPanel.
10864     * @type Roo.Element
10865     */
10866     this.el = Roo.get(container, true);
10867     if(config){
10868         if(typeof config == "boolean"){
10869             this.tabPosition = config ? "bottom" : "top";
10870         }else{
10871             Roo.apply(this, config);
10872         }
10873     }
10874     if(this.tabPosition == "bottom"){
10875         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10876         this.el.addClass("x-tabs-bottom");
10877     }
10878     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10879     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10880     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10881     if(Roo.isIE){
10882         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10883     }
10884     if(this.tabPosition != "bottom"){
10885         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10886          * @type Roo.Element
10887          */
10888         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10889         this.el.addClass("x-tabs-top");
10890     }
10891     this.items = [];
10892
10893     this.bodyEl.setStyle("position", "relative");
10894
10895     this.active = null;
10896     this.activateDelegate = this.activate.createDelegate(this);
10897
10898     this.addEvents({
10899         /**
10900          * @event tabchange
10901          * Fires when the active tab changes
10902          * @param {Roo.TabPanel} this
10903          * @param {Roo.TabPanelItem} activePanel The new active tab
10904          */
10905         "tabchange": true,
10906         /**
10907          * @event beforetabchange
10908          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10909          * @param {Roo.TabPanel} this
10910          * @param {Object} e Set cancel to true on this object to cancel the tab change
10911          * @param {Roo.TabPanelItem} tab The tab being changed to
10912          */
10913         "beforetabchange" : true
10914     });
10915
10916     Roo.EventManager.onWindowResize(this.onResize, this);
10917     this.cpad = this.el.getPadding("lr");
10918     this.hiddenCount = 0;
10919
10920
10921     // toolbar on the tabbar support...
10922     if (this.toolbar) {
10923         var tcfg = this.toolbar;
10924         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10925         this.toolbar = new Roo.Toolbar(tcfg);
10926         if (Roo.isSafari) {
10927             var tbl = tcfg.container.child('table', true);
10928             tbl.setAttribute('width', '100%');
10929         }
10930         
10931     }
10932    
10933
10934
10935     Roo.TabPanel.superclass.constructor.call(this);
10936 };
10937
10938 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10939     /*
10940      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10941      */
10942     tabPosition : "top",
10943     /*
10944      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10945      */
10946     currentTabWidth : 0,
10947     /*
10948      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10949      */
10950     minTabWidth : 40,
10951     /*
10952      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10953      */
10954     maxTabWidth : 250,
10955     /*
10956      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10957      */
10958     preferredTabWidth : 175,
10959     /*
10960      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10961      */
10962     resizeTabs : false,
10963     /*
10964      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10965      */
10966     monitorResize : true,
10967     /*
10968      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10969      */
10970     toolbar : false,
10971
10972     /**
10973      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10974      * @param {String} id The id of the div to use <b>or create</b>
10975      * @param {String} text The text for the tab
10976      * @param {String} content (optional) Content to put in the TabPanelItem body
10977      * @param {Boolean} closable (optional) True to create a close icon on the tab
10978      * @return {Roo.TabPanelItem} The created TabPanelItem
10979      */
10980     addTab : function(id, text, content, closable){
10981         var item = new Roo.TabPanelItem(this, id, text, closable);
10982         this.addTabItem(item);
10983         if(content){
10984             item.setContent(content);
10985         }
10986         return item;
10987     },
10988
10989     /**
10990      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10991      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10992      * @return {Roo.TabPanelItem}
10993      */
10994     getTab : function(id){
10995         return this.items[id];
10996     },
10997
10998     /**
10999      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11000      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11001      */
11002     hideTab : function(id){
11003         var t = this.items[id];
11004         if(!t.isHidden()){
11005            t.setHidden(true);
11006            this.hiddenCount++;
11007            this.autoSizeTabs();
11008         }
11009     },
11010
11011     /**
11012      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11013      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11014      */
11015     unhideTab : function(id){
11016         var t = this.items[id];
11017         if(t.isHidden()){
11018            t.setHidden(false);
11019            this.hiddenCount--;
11020            this.autoSizeTabs();
11021         }
11022     },
11023
11024     /**
11025      * Adds an existing {@link Roo.TabPanelItem}.
11026      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11027      */
11028     addTabItem : function(item){
11029         this.items[item.id] = item;
11030         this.items.push(item);
11031         if(this.resizeTabs){
11032            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11033            this.autoSizeTabs();
11034         }else{
11035             item.autoSize();
11036         }
11037     },
11038
11039     /**
11040      * Removes a {@link Roo.TabPanelItem}.
11041      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11042      */
11043     removeTab : function(id){
11044         var items = this.items;
11045         var tab = items[id];
11046         if(!tab) { return; }
11047         var index = items.indexOf(tab);
11048         if(this.active == tab && items.length > 1){
11049             var newTab = this.getNextAvailable(index);
11050             if(newTab) {
11051                 newTab.activate();
11052             }
11053         }
11054         this.stripEl.dom.removeChild(tab.pnode.dom);
11055         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11056             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11057         }
11058         items.splice(index, 1);
11059         delete this.items[tab.id];
11060         tab.fireEvent("close", tab);
11061         tab.purgeListeners();
11062         this.autoSizeTabs();
11063     },
11064
11065     getNextAvailable : function(start){
11066         var items = this.items;
11067         var index = start;
11068         // look for a next tab that will slide over to
11069         // replace the one being removed
11070         while(index < items.length){
11071             var item = items[++index];
11072             if(item && !item.isHidden()){
11073                 return item;
11074             }
11075         }
11076         // if one isn't found select the previous tab (on the left)
11077         index = start;
11078         while(index >= 0){
11079             var item = items[--index];
11080             if(item && !item.isHidden()){
11081                 return item;
11082             }
11083         }
11084         return null;
11085     },
11086
11087     /**
11088      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11089      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11090      */
11091     disableTab : function(id){
11092         var tab = this.items[id];
11093         if(tab && this.active != tab){
11094             tab.disable();
11095         }
11096     },
11097
11098     /**
11099      * Enables a {@link Roo.TabPanelItem} that is disabled.
11100      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11101      */
11102     enableTab : function(id){
11103         var tab = this.items[id];
11104         tab.enable();
11105     },
11106
11107     /**
11108      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11109      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11110      * @return {Roo.TabPanelItem} The TabPanelItem.
11111      */
11112     activate : function(id){
11113         var tab = this.items[id];
11114         if(!tab){
11115             return null;
11116         }
11117         if(tab == this.active || tab.disabled){
11118             return tab;
11119         }
11120         var e = {};
11121         this.fireEvent("beforetabchange", this, e, tab);
11122         if(e.cancel !== true && !tab.disabled){
11123             if(this.active){
11124                 this.active.hide();
11125             }
11126             this.active = this.items[id];
11127             this.active.show();
11128             this.fireEvent("tabchange", this, this.active);
11129         }
11130         return tab;
11131     },
11132
11133     /**
11134      * Gets the active {@link Roo.TabPanelItem}.
11135      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11136      */
11137     getActiveTab : function(){
11138         return this.active;
11139     },
11140
11141     /**
11142      * Updates the tab body element to fit the height of the container element
11143      * for overflow scrolling
11144      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11145      */
11146     syncHeight : function(targetHeight){
11147         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11148         var bm = this.bodyEl.getMargins();
11149         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11150         this.bodyEl.setHeight(newHeight);
11151         return newHeight;
11152     },
11153
11154     onResize : function(){
11155         if(this.monitorResize){
11156             this.autoSizeTabs();
11157         }
11158     },
11159
11160     /**
11161      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11162      */
11163     beginUpdate : function(){
11164         this.updating = true;
11165     },
11166
11167     /**
11168      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11169      */
11170     endUpdate : function(){
11171         this.updating = false;
11172         this.autoSizeTabs();
11173     },
11174
11175     /**
11176      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11177      */
11178     autoSizeTabs : function(){
11179         var count = this.items.length;
11180         var vcount = count - this.hiddenCount;
11181         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11182         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11183         var availWidth = Math.floor(w / vcount);
11184         var b = this.stripBody;
11185         if(b.getWidth() > w){
11186             var tabs = this.items;
11187             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11188             if(availWidth < this.minTabWidth){
11189                 /*if(!this.sleft){    // incomplete scrolling code
11190                     this.createScrollButtons();
11191                 }
11192                 this.showScroll();
11193                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11194             }
11195         }else{
11196             if(this.currentTabWidth < this.preferredTabWidth){
11197                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11198             }
11199         }
11200     },
11201
11202     /**
11203      * Returns the number of tabs in this TabPanel.
11204      * @return {Number}
11205      */
11206      getCount : function(){
11207          return this.items.length;
11208      },
11209
11210     /**
11211      * Resizes all the tabs to the passed width
11212      * @param {Number} The new width
11213      */
11214     setTabWidth : function(width){
11215         this.currentTabWidth = width;
11216         for(var i = 0, len = this.items.length; i < len; i++) {
11217                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11218         }
11219     },
11220
11221     /**
11222      * Destroys this TabPanel
11223      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11224      */
11225     destroy : function(removeEl){
11226         Roo.EventManager.removeResizeListener(this.onResize, this);
11227         for(var i = 0, len = this.items.length; i < len; i++){
11228             this.items[i].purgeListeners();
11229         }
11230         if(removeEl === true){
11231             this.el.update("");
11232             this.el.remove();
11233         }
11234     }
11235 });
11236
11237 /**
11238  * @class Roo.TabPanelItem
11239  * @extends Roo.util.Observable
11240  * Represents an individual item (tab plus body) in a TabPanel.
11241  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11242  * @param {String} id The id of this TabPanelItem
11243  * @param {String} text The text for the tab of this TabPanelItem
11244  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11245  */
11246 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11247     /**
11248      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11249      * @type Roo.TabPanel
11250      */
11251     this.tabPanel = tabPanel;
11252     /**
11253      * The id for this TabPanelItem
11254      * @type String
11255      */
11256     this.id = id;
11257     /** @private */
11258     this.disabled = false;
11259     /** @private */
11260     this.text = text;
11261     /** @private */
11262     this.loaded = false;
11263     this.closable = closable;
11264
11265     /**
11266      * The body element for this TabPanelItem.
11267      * @type Roo.Element
11268      */
11269     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11270     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11271     this.bodyEl.setStyle("display", "block");
11272     this.bodyEl.setStyle("zoom", "1");
11273     this.hideAction();
11274
11275     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11276     /** @private */
11277     this.el = Roo.get(els.el, true);
11278     this.inner = Roo.get(els.inner, true);
11279     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11280     this.pnode = Roo.get(els.el.parentNode, true);
11281     this.el.on("mousedown", this.onTabMouseDown, this);
11282     this.el.on("click", this.onTabClick, this);
11283     /** @private */
11284     if(closable){
11285         var c = Roo.get(els.close, true);
11286         c.dom.title = this.closeText;
11287         c.addClassOnOver("close-over");
11288         c.on("click", this.closeClick, this);
11289      }
11290
11291     this.addEvents({
11292          /**
11293          * @event activate
11294          * Fires when this tab becomes the active tab.
11295          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11296          * @param {Roo.TabPanelItem} this
11297          */
11298         "activate": true,
11299         /**
11300          * @event beforeclose
11301          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11302          * @param {Roo.TabPanelItem} this
11303          * @param {Object} e Set cancel to true on this object to cancel the close.
11304          */
11305         "beforeclose": true,
11306         /**
11307          * @event close
11308          * Fires when this tab is closed.
11309          * @param {Roo.TabPanelItem} this
11310          */
11311          "close": true,
11312         /**
11313          * @event deactivate
11314          * Fires when this tab is no longer the active tab.
11315          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11316          * @param {Roo.TabPanelItem} this
11317          */
11318          "deactivate" : true
11319     });
11320     this.hidden = false;
11321
11322     Roo.TabPanelItem.superclass.constructor.call(this);
11323 };
11324
11325 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11326     purgeListeners : function(){
11327        Roo.util.Observable.prototype.purgeListeners.call(this);
11328        this.el.removeAllListeners();
11329     },
11330     /**
11331      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11332      */
11333     show : function(){
11334         this.pnode.addClass("on");
11335         this.showAction();
11336         if(Roo.isOpera){
11337             this.tabPanel.stripWrap.repaint();
11338         }
11339         this.fireEvent("activate", this.tabPanel, this);
11340     },
11341
11342     /**
11343      * Returns true if this tab is the active tab.
11344      * @return {Boolean}
11345      */
11346     isActive : function(){
11347         return this.tabPanel.getActiveTab() == this;
11348     },
11349
11350     /**
11351      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11352      */
11353     hide : function(){
11354         this.pnode.removeClass("on");
11355         this.hideAction();
11356         this.fireEvent("deactivate", this.tabPanel, this);
11357     },
11358
11359     hideAction : function(){
11360         this.bodyEl.hide();
11361         this.bodyEl.setStyle("position", "absolute");
11362         this.bodyEl.setLeft("-20000px");
11363         this.bodyEl.setTop("-20000px");
11364     },
11365
11366     showAction : function(){
11367         this.bodyEl.setStyle("position", "relative");
11368         this.bodyEl.setTop("");
11369         this.bodyEl.setLeft("");
11370         this.bodyEl.show();
11371     },
11372
11373     /**
11374      * Set the tooltip for the tab.
11375      * @param {String} tooltip The tab's tooltip
11376      */
11377     setTooltip : function(text){
11378         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11379             this.textEl.dom.qtip = text;
11380             this.textEl.dom.removeAttribute('title');
11381         }else{
11382             this.textEl.dom.title = text;
11383         }
11384     },
11385
11386     onTabClick : function(e){
11387         e.preventDefault();
11388         this.tabPanel.activate(this.id);
11389     },
11390
11391     onTabMouseDown : function(e){
11392         e.preventDefault();
11393         this.tabPanel.activate(this.id);
11394     },
11395
11396     getWidth : function(){
11397         return this.inner.getWidth();
11398     },
11399
11400     setWidth : function(width){
11401         var iwidth = width - this.pnode.getPadding("lr");
11402         this.inner.setWidth(iwidth);
11403         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11404         this.pnode.setWidth(width);
11405     },
11406
11407     /**
11408      * Show or hide the tab
11409      * @param {Boolean} hidden True to hide or false to show.
11410      */
11411     setHidden : function(hidden){
11412         this.hidden = hidden;
11413         this.pnode.setStyle("display", hidden ? "none" : "");
11414     },
11415
11416     /**
11417      * Returns true if this tab is "hidden"
11418      * @return {Boolean}
11419      */
11420     isHidden : function(){
11421         return this.hidden;
11422     },
11423
11424     /**
11425      * Returns the text for this tab
11426      * @return {String}
11427      */
11428     getText : function(){
11429         return this.text;
11430     },
11431
11432     autoSize : function(){
11433         //this.el.beginMeasure();
11434         this.textEl.setWidth(1);
11435         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11436         //this.el.endMeasure();
11437     },
11438
11439     /**
11440      * Sets the text for the tab (Note: this also sets the tooltip text)
11441      * @param {String} text The tab's text and tooltip
11442      */
11443     setText : function(text){
11444         this.text = text;
11445         this.textEl.update(text);
11446         this.setTooltip(text);
11447         if(!this.tabPanel.resizeTabs){
11448             this.autoSize();
11449         }
11450     },
11451     /**
11452      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11453      */
11454     activate : function(){
11455         this.tabPanel.activate(this.id);
11456     },
11457
11458     /**
11459      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11460      */
11461     disable : function(){
11462         if(this.tabPanel.active != this){
11463             this.disabled = true;
11464             this.pnode.addClass("disabled");
11465         }
11466     },
11467
11468     /**
11469      * Enables this TabPanelItem if it was previously disabled.
11470      */
11471     enable : function(){
11472         this.disabled = false;
11473         this.pnode.removeClass("disabled");
11474     },
11475
11476     /**
11477      * Sets the content for this TabPanelItem.
11478      * @param {String} content The content
11479      * @param {Boolean} loadScripts true to look for and load scripts
11480      */
11481     setContent : function(content, loadScripts){
11482         this.bodyEl.update(content, loadScripts);
11483     },
11484
11485     /**
11486      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11487      * @return {Roo.UpdateManager} The UpdateManager
11488      */
11489     getUpdateManager : function(){
11490         return this.bodyEl.getUpdateManager();
11491     },
11492
11493     /**
11494      * Set a URL to be used to load the content for this TabPanelItem.
11495      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11496      * @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)
11497      * @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)
11498      * @return {Roo.UpdateManager} The UpdateManager
11499      */
11500     setUrl : function(url, params, loadOnce){
11501         if(this.refreshDelegate){
11502             this.un('activate', this.refreshDelegate);
11503         }
11504         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11505         this.on("activate", this.refreshDelegate);
11506         return this.bodyEl.getUpdateManager();
11507     },
11508
11509     /** @private */
11510     _handleRefresh : function(url, params, loadOnce){
11511         if(!loadOnce || !this.loaded){
11512             var updater = this.bodyEl.getUpdateManager();
11513             updater.update(url, params, this._setLoaded.createDelegate(this));
11514         }
11515     },
11516
11517     /**
11518      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11519      *   Will fail silently if the setUrl method has not been called.
11520      *   This does not activate the panel, just updates its content.
11521      */
11522     refresh : function(){
11523         if(this.refreshDelegate){
11524            this.loaded = false;
11525            this.refreshDelegate();
11526         }
11527     },
11528
11529     /** @private */
11530     _setLoaded : function(){
11531         this.loaded = true;
11532     },
11533
11534     /** @private */
11535     closeClick : function(e){
11536         var o = {};
11537         e.stopEvent();
11538         this.fireEvent("beforeclose", this, o);
11539         if(o.cancel !== true){
11540             this.tabPanel.removeTab(this.id);
11541         }
11542     },
11543     /**
11544      * The text displayed in the tooltip for the close icon.
11545      * @type String
11546      */
11547     closeText : "Close this tab"
11548 });
11549
11550 /** @private */
11551 Roo.TabPanel.prototype.createStrip = function(container){
11552     var strip = document.createElement("div");
11553     strip.className = "x-tabs-wrap";
11554     container.appendChild(strip);
11555     return strip;
11556 };
11557 /** @private */
11558 Roo.TabPanel.prototype.createStripList = function(strip){
11559     // div wrapper for retard IE
11560     // returns the "tr" element.
11561     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11562         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11563         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11564     return strip.firstChild.firstChild.firstChild.firstChild;
11565 };
11566 /** @private */
11567 Roo.TabPanel.prototype.createBody = function(container){
11568     var body = document.createElement("div");
11569     Roo.id(body, "tab-body");
11570     Roo.fly(body).addClass("x-tabs-body");
11571     container.appendChild(body);
11572     return body;
11573 };
11574 /** @private */
11575 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11576     var body = Roo.getDom(id);
11577     if(!body){
11578         body = document.createElement("div");
11579         body.id = id;
11580     }
11581     Roo.fly(body).addClass("x-tabs-item-body");
11582     bodyEl.insertBefore(body, bodyEl.firstChild);
11583     return body;
11584 };
11585 /** @private */
11586 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11587     var td = document.createElement("td");
11588     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11589     //stripEl.appendChild(td);
11590     if(closable){
11591         td.className = "x-tabs-closable";
11592         if(!this.closeTpl){
11593             this.closeTpl = new Roo.Template(
11594                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11595                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11596                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11597             );
11598         }
11599         var el = this.closeTpl.overwrite(td, {"text": text});
11600         var close = el.getElementsByTagName("div")[0];
11601         var inner = el.getElementsByTagName("em")[0];
11602         return {"el": el, "close": close, "inner": inner};
11603     } else {
11604         if(!this.tabTpl){
11605             this.tabTpl = new Roo.Template(
11606                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11607                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11608             );
11609         }
11610         var el = this.tabTpl.overwrite(td, {"text": text});
11611         var inner = el.getElementsByTagName("em")[0];
11612         return {"el": el, "inner": inner};
11613     }
11614 };/*
11615  * Based on:
11616  * Ext JS Library 1.1.1
11617  * Copyright(c) 2006-2007, Ext JS, LLC.
11618  *
11619  * Originally Released Under LGPL - original licence link has changed is not relivant.
11620  *
11621  * Fork - LGPL
11622  * <script type="text/javascript">
11623  */
11624
11625 /**
11626  * @class Roo.Button
11627  * @extends Roo.util.Observable
11628  * Simple Button class
11629  * @cfg {String} text The button text
11630  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11631  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11632  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11633  * @cfg {Object} scope The scope of the handler
11634  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11635  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11636  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11637  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11638  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11639  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11640    applies if enableToggle = true)
11641  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11642  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11643   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11644  * @constructor
11645  * Create a new button
11646  * @param {Object} config The config object
11647  */
11648 Roo.Button = function(renderTo, config)
11649 {
11650     if (!config) {
11651         config = renderTo;
11652         renderTo = config.renderTo || false;
11653     }
11654     
11655     Roo.apply(this, config);
11656     this.addEvents({
11657         /**
11658              * @event click
11659              * Fires when this button is clicked
11660              * @param {Button} this
11661              * @param {EventObject} e The click event
11662              */
11663             "click" : true,
11664         /**
11665              * @event toggle
11666              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11667              * @param {Button} this
11668              * @param {Boolean} pressed
11669              */
11670             "toggle" : true,
11671         /**
11672              * @event mouseover
11673              * Fires when the mouse hovers over the button
11674              * @param {Button} this
11675              * @param {Event} e The event object
11676              */
11677         'mouseover' : true,
11678         /**
11679              * @event mouseout
11680              * Fires when the mouse exits the button
11681              * @param {Button} this
11682              * @param {Event} e The event object
11683              */
11684         'mouseout': true,
11685          /**
11686              * @event render
11687              * Fires when the button is rendered
11688              * @param {Button} this
11689              */
11690         'render': true
11691     });
11692     if(this.menu){
11693         this.menu = Roo.menu.MenuMgr.get(this.menu);
11694     }
11695     // register listeners first!!  - so render can be captured..
11696     Roo.util.Observable.call(this);
11697     if(renderTo){
11698         this.render(renderTo);
11699     }
11700     
11701   
11702 };
11703
11704 Roo.extend(Roo.Button, Roo.util.Observable, {
11705     /**
11706      * 
11707      */
11708     
11709     /**
11710      * Read-only. True if this button is hidden
11711      * @type Boolean
11712      */
11713     hidden : false,
11714     /**
11715      * Read-only. True if this button is disabled
11716      * @type Boolean
11717      */
11718     disabled : false,
11719     /**
11720      * Read-only. True if this button is pressed (only if enableToggle = true)
11721      * @type Boolean
11722      */
11723     pressed : false,
11724
11725     /**
11726      * @cfg {Number} tabIndex 
11727      * The DOM tabIndex for this button (defaults to undefined)
11728      */
11729     tabIndex : undefined,
11730
11731     /**
11732      * @cfg {Boolean} enableToggle
11733      * True to enable pressed/not pressed toggling (defaults to false)
11734      */
11735     enableToggle: false,
11736     /**
11737      * @cfg {Mixed} menu
11738      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11739      */
11740     menu : undefined,
11741     /**
11742      * @cfg {String} menuAlign
11743      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11744      */
11745     menuAlign : "tl-bl?",
11746
11747     /**
11748      * @cfg {String} iconCls
11749      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11750      */
11751     iconCls : undefined,
11752     /**
11753      * @cfg {String} type
11754      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11755      */
11756     type : 'button',
11757
11758     // private
11759     menuClassTarget: 'tr',
11760
11761     /**
11762      * @cfg {String} clickEvent
11763      * The type of event to map to the button's event handler (defaults to 'click')
11764      */
11765     clickEvent : 'click',
11766
11767     /**
11768      * @cfg {Boolean} handleMouseEvents
11769      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11770      */
11771     handleMouseEvents : true,
11772
11773     /**
11774      * @cfg {String} tooltipType
11775      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11776      */
11777     tooltipType : 'qtip',
11778
11779     /**
11780      * @cfg {String} cls
11781      * A CSS class to apply to the button's main element.
11782      */
11783     
11784     /**
11785      * @cfg {Roo.Template} template (Optional)
11786      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11787      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11788      * require code modifications if required elements (e.g. a button) aren't present.
11789      */
11790
11791     // private
11792     render : function(renderTo){
11793         var btn;
11794         if(this.hideParent){
11795             this.parentEl = Roo.get(renderTo);
11796         }
11797         if(!this.dhconfig){
11798             if(!this.template){
11799                 if(!Roo.Button.buttonTemplate){
11800                     // hideous table template
11801                     Roo.Button.buttonTemplate = new Roo.Template(
11802                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11803                         '<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>',
11804                         "</tr></tbody></table>");
11805                 }
11806                 this.template = Roo.Button.buttonTemplate;
11807             }
11808             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11809             var btnEl = btn.child("button:first");
11810             btnEl.on('focus', this.onFocus, this);
11811             btnEl.on('blur', this.onBlur, this);
11812             if(this.cls){
11813                 btn.addClass(this.cls);
11814             }
11815             if(this.icon){
11816                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11817             }
11818             if(this.iconCls){
11819                 btnEl.addClass(this.iconCls);
11820                 if(!this.cls){
11821                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11822                 }
11823             }
11824             if(this.tabIndex !== undefined){
11825                 btnEl.dom.tabIndex = this.tabIndex;
11826             }
11827             if(this.tooltip){
11828                 if(typeof this.tooltip == 'object'){
11829                     Roo.QuickTips.tips(Roo.apply({
11830                           target: btnEl.id
11831                     }, this.tooltip));
11832                 } else {
11833                     btnEl.dom[this.tooltipType] = this.tooltip;
11834                 }
11835             }
11836         }else{
11837             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11838         }
11839         this.el = btn;
11840         if(this.id){
11841             this.el.dom.id = this.el.id = this.id;
11842         }
11843         if(this.menu){
11844             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11845             this.menu.on("show", this.onMenuShow, this);
11846             this.menu.on("hide", this.onMenuHide, this);
11847         }
11848         btn.addClass("x-btn");
11849         if(Roo.isIE && !Roo.isIE7){
11850             this.autoWidth.defer(1, this);
11851         }else{
11852             this.autoWidth();
11853         }
11854         if(this.handleMouseEvents){
11855             btn.on("mouseover", this.onMouseOver, this);
11856             btn.on("mouseout", this.onMouseOut, this);
11857             btn.on("mousedown", this.onMouseDown, this);
11858         }
11859         btn.on(this.clickEvent, this.onClick, this);
11860         //btn.on("mouseup", this.onMouseUp, this);
11861         if(this.hidden){
11862             this.hide();
11863         }
11864         if(this.disabled){
11865             this.disable();
11866         }
11867         Roo.ButtonToggleMgr.register(this);
11868         if(this.pressed){
11869             this.el.addClass("x-btn-pressed");
11870         }
11871         if(this.repeat){
11872             var repeater = new Roo.util.ClickRepeater(btn,
11873                 typeof this.repeat == "object" ? this.repeat : {}
11874             );
11875             repeater.on("click", this.onClick,  this);
11876         }
11877         
11878         this.fireEvent('render', this);
11879         
11880     },
11881     /**
11882      * Returns the button's underlying element
11883      * @return {Roo.Element} The element
11884      */
11885     getEl : function(){
11886         return this.el;  
11887     },
11888     
11889     /**
11890      * Destroys this Button and removes any listeners.
11891      */
11892     destroy : function(){
11893         Roo.ButtonToggleMgr.unregister(this);
11894         this.el.removeAllListeners();
11895         this.purgeListeners();
11896         this.el.remove();
11897     },
11898
11899     // private
11900     autoWidth : function(){
11901         if(this.el){
11902             this.el.setWidth("auto");
11903             if(Roo.isIE7 && Roo.isStrict){
11904                 var ib = this.el.child('button');
11905                 if(ib && ib.getWidth() > 20){
11906                     ib.clip();
11907                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11908                 }
11909             }
11910             if(this.minWidth){
11911                 if(this.hidden){
11912                     this.el.beginMeasure();
11913                 }
11914                 if(this.el.getWidth() < this.minWidth){
11915                     this.el.setWidth(this.minWidth);
11916                 }
11917                 if(this.hidden){
11918                     this.el.endMeasure();
11919                 }
11920             }
11921         }
11922     },
11923
11924     /**
11925      * Assigns this button's click handler
11926      * @param {Function} handler The function to call when the button is clicked
11927      * @param {Object} scope (optional) Scope for the function passed in
11928      */
11929     setHandler : function(handler, scope){
11930         this.handler = handler;
11931         this.scope = scope;  
11932     },
11933     
11934     /**
11935      * Sets this button's text
11936      * @param {String} text The button text
11937      */
11938     setText : function(text){
11939         this.text = text;
11940         if(this.el){
11941             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11942         }
11943         this.autoWidth();
11944     },
11945     
11946     /**
11947      * Gets the text for this button
11948      * @return {String} The button text
11949      */
11950     getText : function(){
11951         return this.text;  
11952     },
11953     
11954     /**
11955      * Show this button
11956      */
11957     show: function(){
11958         this.hidden = false;
11959         if(this.el){
11960             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11961         }
11962     },
11963     
11964     /**
11965      * Hide this button
11966      */
11967     hide: function(){
11968         this.hidden = true;
11969         if(this.el){
11970             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11971         }
11972     },
11973     
11974     /**
11975      * Convenience function for boolean show/hide
11976      * @param {Boolean} visible True to show, false to hide
11977      */
11978     setVisible: function(visible){
11979         if(visible) {
11980             this.show();
11981         }else{
11982             this.hide();
11983         }
11984     },
11985     
11986     /**
11987      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11988      * @param {Boolean} state (optional) Force a particular state
11989      */
11990     toggle : function(state){
11991         state = state === undefined ? !this.pressed : state;
11992         if(state != this.pressed){
11993             if(state){
11994                 this.el.addClass("x-btn-pressed");
11995                 this.pressed = true;
11996                 this.fireEvent("toggle", this, true);
11997             }else{
11998                 this.el.removeClass("x-btn-pressed");
11999                 this.pressed = false;
12000                 this.fireEvent("toggle", this, false);
12001             }
12002             if(this.toggleHandler){
12003                 this.toggleHandler.call(this.scope || this, this, state);
12004             }
12005         }
12006     },
12007     
12008     /**
12009      * Focus the button
12010      */
12011     focus : function(){
12012         this.el.child('button:first').focus();
12013     },
12014     
12015     /**
12016      * Disable this button
12017      */
12018     disable : function(){
12019         if(this.el){
12020             this.el.addClass("x-btn-disabled");
12021         }
12022         this.disabled = true;
12023     },
12024     
12025     /**
12026      * Enable this button
12027      */
12028     enable : function(){
12029         if(this.el){
12030             this.el.removeClass("x-btn-disabled");
12031         }
12032         this.disabled = false;
12033     },
12034
12035     /**
12036      * Convenience function for boolean enable/disable
12037      * @param {Boolean} enabled True to enable, false to disable
12038      */
12039     setDisabled : function(v){
12040         this[v !== true ? "enable" : "disable"]();
12041     },
12042
12043     // private
12044     onClick : function(e){
12045         if(e){
12046             e.preventDefault();
12047         }
12048         if(e.button != 0){
12049             return;
12050         }
12051         if(!this.disabled){
12052             if(this.enableToggle){
12053                 this.toggle();
12054             }
12055             if(this.menu && !this.menu.isVisible()){
12056                 this.menu.show(this.el, this.menuAlign);
12057             }
12058             this.fireEvent("click", this, e);
12059             if(this.handler){
12060                 this.el.removeClass("x-btn-over");
12061                 this.handler.call(this.scope || this, this, e);
12062             }
12063         }
12064     },
12065     // private
12066     onMouseOver : function(e){
12067         if(!this.disabled){
12068             this.el.addClass("x-btn-over");
12069             this.fireEvent('mouseover', this, e);
12070         }
12071     },
12072     // private
12073     onMouseOut : function(e){
12074         if(!e.within(this.el,  true)){
12075             this.el.removeClass("x-btn-over");
12076             this.fireEvent('mouseout', this, e);
12077         }
12078     },
12079     // private
12080     onFocus : function(e){
12081         if(!this.disabled){
12082             this.el.addClass("x-btn-focus");
12083         }
12084     },
12085     // private
12086     onBlur : function(e){
12087         this.el.removeClass("x-btn-focus");
12088     },
12089     // private
12090     onMouseDown : function(e){
12091         if(!this.disabled && e.button == 0){
12092             this.el.addClass("x-btn-click");
12093             Roo.get(document).on('mouseup', this.onMouseUp, this);
12094         }
12095     },
12096     // private
12097     onMouseUp : function(e){
12098         if(e.button == 0){
12099             this.el.removeClass("x-btn-click");
12100             Roo.get(document).un('mouseup', this.onMouseUp, this);
12101         }
12102     },
12103     // private
12104     onMenuShow : function(e){
12105         this.el.addClass("x-btn-menu-active");
12106     },
12107     // private
12108     onMenuHide : function(e){
12109         this.el.removeClass("x-btn-menu-active");
12110     }   
12111 });
12112
12113 // Private utility class used by Button
12114 Roo.ButtonToggleMgr = function(){
12115    var groups = {};
12116    
12117    function toggleGroup(btn, state){
12118        if(state){
12119            var g = groups[btn.toggleGroup];
12120            for(var i = 0, l = g.length; i < l; i++){
12121                if(g[i] != btn){
12122                    g[i].toggle(false);
12123                }
12124            }
12125        }
12126    }
12127    
12128    return {
12129        register : function(btn){
12130            if(!btn.toggleGroup){
12131                return;
12132            }
12133            var g = groups[btn.toggleGroup];
12134            if(!g){
12135                g = groups[btn.toggleGroup] = [];
12136            }
12137            g.push(btn);
12138            btn.on("toggle", toggleGroup);
12139        },
12140        
12141        unregister : function(btn){
12142            if(!btn.toggleGroup){
12143                return;
12144            }
12145            var g = groups[btn.toggleGroup];
12146            if(g){
12147                g.remove(btn);
12148                btn.un("toggle", toggleGroup);
12149            }
12150        }
12151    };
12152 }();/*
12153  * Based on:
12154  * Ext JS Library 1.1.1
12155  * Copyright(c) 2006-2007, Ext JS, LLC.
12156  *
12157  * Originally Released Under LGPL - original licence link has changed is not relivant.
12158  *
12159  * Fork - LGPL
12160  * <script type="text/javascript">
12161  */
12162  
12163 /**
12164  * @class Roo.SplitButton
12165  * @extends Roo.Button
12166  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12167  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12168  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12169  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12170  * @cfg {String} arrowTooltip The title attribute of the arrow
12171  * @constructor
12172  * Create a new menu button
12173  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12174  * @param {Object} config The config object
12175  */
12176 Roo.SplitButton = function(renderTo, config){
12177     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12178     /**
12179      * @event arrowclick
12180      * Fires when this button's arrow is clicked
12181      * @param {SplitButton} this
12182      * @param {EventObject} e The click event
12183      */
12184     this.addEvents({"arrowclick":true});
12185 };
12186
12187 Roo.extend(Roo.SplitButton, Roo.Button, {
12188     render : function(renderTo){
12189         // this is one sweet looking template!
12190         var tpl = new Roo.Template(
12191             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12192             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12193             '<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>',
12194             "</tbody></table></td><td>",
12195             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12196             '<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>',
12197             "</tbody></table></td></tr></table>"
12198         );
12199         var btn = tpl.append(renderTo, [this.text, this.type], true);
12200         var btnEl = btn.child("button");
12201         if(this.cls){
12202             btn.addClass(this.cls);
12203         }
12204         if(this.icon){
12205             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12206         }
12207         if(this.iconCls){
12208             btnEl.addClass(this.iconCls);
12209             if(!this.cls){
12210                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12211             }
12212         }
12213         this.el = btn;
12214         if(this.handleMouseEvents){
12215             btn.on("mouseover", this.onMouseOver, this);
12216             btn.on("mouseout", this.onMouseOut, this);
12217             btn.on("mousedown", this.onMouseDown, this);
12218             btn.on("mouseup", this.onMouseUp, this);
12219         }
12220         btn.on(this.clickEvent, this.onClick, this);
12221         if(this.tooltip){
12222             if(typeof this.tooltip == 'object'){
12223                 Roo.QuickTips.tips(Roo.apply({
12224                       target: btnEl.id
12225                 }, this.tooltip));
12226             } else {
12227                 btnEl.dom[this.tooltipType] = this.tooltip;
12228             }
12229         }
12230         if(this.arrowTooltip){
12231             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12232         }
12233         if(this.hidden){
12234             this.hide();
12235         }
12236         if(this.disabled){
12237             this.disable();
12238         }
12239         if(this.pressed){
12240             this.el.addClass("x-btn-pressed");
12241         }
12242         if(Roo.isIE && !Roo.isIE7){
12243             this.autoWidth.defer(1, this);
12244         }else{
12245             this.autoWidth();
12246         }
12247         if(this.menu){
12248             this.menu.on("show", this.onMenuShow, this);
12249             this.menu.on("hide", this.onMenuHide, this);
12250         }
12251         this.fireEvent('render', this);
12252     },
12253
12254     // private
12255     autoWidth : function(){
12256         if(this.el){
12257             var tbl = this.el.child("table:first");
12258             var tbl2 = this.el.child("table:last");
12259             this.el.setWidth("auto");
12260             tbl.setWidth("auto");
12261             if(Roo.isIE7 && Roo.isStrict){
12262                 var ib = this.el.child('button:first');
12263                 if(ib && ib.getWidth() > 20){
12264                     ib.clip();
12265                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12266                 }
12267             }
12268             if(this.minWidth){
12269                 if(this.hidden){
12270                     this.el.beginMeasure();
12271                 }
12272                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12273                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12274                 }
12275                 if(this.hidden){
12276                     this.el.endMeasure();
12277                 }
12278             }
12279             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12280         } 
12281     },
12282     /**
12283      * Sets this button's click handler
12284      * @param {Function} handler The function to call when the button is clicked
12285      * @param {Object} scope (optional) Scope for the function passed above
12286      */
12287     setHandler : function(handler, scope){
12288         this.handler = handler;
12289         this.scope = scope;  
12290     },
12291     
12292     /**
12293      * Sets this button's arrow click handler
12294      * @param {Function} handler The function to call when the arrow is clicked
12295      * @param {Object} scope (optional) Scope for the function passed above
12296      */
12297     setArrowHandler : function(handler, scope){
12298         this.arrowHandler = handler;
12299         this.scope = scope;  
12300     },
12301     
12302     /**
12303      * Focus the button
12304      */
12305     focus : function(){
12306         if(this.el){
12307             this.el.child("button:first").focus();
12308         }
12309     },
12310
12311     // private
12312     onClick : function(e){
12313         e.preventDefault();
12314         if(!this.disabled){
12315             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12316                 if(this.menu && !this.menu.isVisible()){
12317                     this.menu.show(this.el, this.menuAlign);
12318                 }
12319                 this.fireEvent("arrowclick", this, e);
12320                 if(this.arrowHandler){
12321                     this.arrowHandler.call(this.scope || this, this, e);
12322                 }
12323             }else{
12324                 this.fireEvent("click", this, e);
12325                 if(this.handler){
12326                     this.handler.call(this.scope || this, this, e);
12327                 }
12328             }
12329         }
12330     },
12331     // private
12332     onMouseDown : function(e){
12333         if(!this.disabled){
12334             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12335         }
12336     },
12337     // private
12338     onMouseUp : function(e){
12339         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12340     }   
12341 });
12342
12343
12344 // backwards compat
12345 Roo.MenuButton = Roo.SplitButton;/*
12346  * Based on:
12347  * Ext JS Library 1.1.1
12348  * Copyright(c) 2006-2007, Ext JS, LLC.
12349  *
12350  * Originally Released Under LGPL - original licence link has changed is not relivant.
12351  *
12352  * Fork - LGPL
12353  * <script type="text/javascript">
12354  */
12355
12356 /**
12357  * @class Roo.Toolbar
12358  * Basic Toolbar class.
12359  * @constructor
12360  * Creates a new Toolbar
12361  * @param {Object} container The config object
12362  */ 
12363 Roo.Toolbar = function(container, buttons, config)
12364 {
12365     /// old consturctor format still supported..
12366     if(container instanceof Array){ // omit the container for later rendering
12367         buttons = container;
12368         config = buttons;
12369         container = null;
12370     }
12371     if (typeof(container) == 'object' && container.xtype) {
12372         config = container;
12373         container = config.container;
12374         buttons = config.buttons || []; // not really - use items!!
12375     }
12376     var xitems = [];
12377     if (config && config.items) {
12378         xitems = config.items;
12379         delete config.items;
12380     }
12381     Roo.apply(this, config);
12382     this.buttons = buttons;
12383     
12384     if(container){
12385         this.render(container);
12386     }
12387     this.xitems = xitems;
12388     Roo.each(xitems, function(b) {
12389         this.add(b);
12390     }, this);
12391     
12392 };
12393
12394 Roo.Toolbar.prototype = {
12395     /**
12396      * @cfg {Array} items
12397      * array of button configs or elements to add (will be converted to a MixedCollection)
12398      */
12399     
12400     /**
12401      * @cfg {String/HTMLElement/Element} container
12402      * The id or element that will contain the toolbar
12403      */
12404     // private
12405     render : function(ct){
12406         this.el = Roo.get(ct);
12407         if(this.cls){
12408             this.el.addClass(this.cls);
12409         }
12410         // using a table allows for vertical alignment
12411         // 100% width is needed by Safari...
12412         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12413         this.tr = this.el.child("tr", true);
12414         var autoId = 0;
12415         this.items = new Roo.util.MixedCollection(false, function(o){
12416             return o.id || ("item" + (++autoId));
12417         });
12418         if(this.buttons){
12419             this.add.apply(this, this.buttons);
12420             delete this.buttons;
12421         }
12422     },
12423
12424     /**
12425      * Adds element(s) to the toolbar -- this function takes a variable number of 
12426      * arguments of mixed type and adds them to the toolbar.
12427      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12428      * <ul>
12429      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12430      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12431      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12432      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12433      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12434      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12435      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12436      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12437      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12438      * </ul>
12439      * @param {Mixed} arg2
12440      * @param {Mixed} etc.
12441      */
12442     add : function(){
12443         var a = arguments, l = a.length;
12444         for(var i = 0; i < l; i++){
12445             this._add(a[i]);
12446         }
12447     },
12448     // private..
12449     _add : function(el) {
12450         
12451         if (el.xtype) {
12452             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12453         }
12454         
12455         if (el.applyTo){ // some kind of form field
12456             return this.addField(el);
12457         } 
12458         if (el.render){ // some kind of Toolbar.Item
12459             return this.addItem(el);
12460         }
12461         if (typeof el == "string"){ // string
12462             if(el == "separator" || el == "-"){
12463                 return this.addSeparator();
12464             }
12465             if (el == " "){
12466                 return this.addSpacer();
12467             }
12468             if(el == "->"){
12469                 return this.addFill();
12470             }
12471             return this.addText(el);
12472             
12473         }
12474         if(el.tagName){ // element
12475             return this.addElement(el);
12476         }
12477         if(typeof el == "object"){ // must be button config?
12478             return this.addButton(el);
12479         }
12480         // and now what?!?!
12481         return false;
12482         
12483     },
12484     
12485     /**
12486      * Add an Xtype element
12487      * @param {Object} xtype Xtype Object
12488      * @return {Object} created Object
12489      */
12490     addxtype : function(e){
12491         return this.add(e);  
12492     },
12493     
12494     /**
12495      * Returns the Element for this toolbar.
12496      * @return {Roo.Element}
12497      */
12498     getEl : function(){
12499         return this.el;  
12500     },
12501     
12502     /**
12503      * Adds a separator
12504      * @return {Roo.Toolbar.Item} The separator item
12505      */
12506     addSeparator : function(){
12507         return this.addItem(new Roo.Toolbar.Separator());
12508     },
12509
12510     /**
12511      * Adds a spacer element
12512      * @return {Roo.Toolbar.Spacer} The spacer item
12513      */
12514     addSpacer : function(){
12515         return this.addItem(new Roo.Toolbar.Spacer());
12516     },
12517
12518     /**
12519      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12520      * @return {Roo.Toolbar.Fill} The fill item
12521      */
12522     addFill : function(){
12523         return this.addItem(new Roo.Toolbar.Fill());
12524     },
12525
12526     /**
12527      * Adds any standard HTML element to the toolbar
12528      * @param {String/HTMLElement/Element} el The element or id of the element to add
12529      * @return {Roo.Toolbar.Item} The element's item
12530      */
12531     addElement : function(el){
12532         return this.addItem(new Roo.Toolbar.Item(el));
12533     },
12534     /**
12535      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12536      * @type Roo.util.MixedCollection  
12537      */
12538     items : false,
12539      
12540     /**
12541      * Adds any Toolbar.Item or subclass
12542      * @param {Roo.Toolbar.Item} item
12543      * @return {Roo.Toolbar.Item} The item
12544      */
12545     addItem : function(item){
12546         var td = this.nextBlock();
12547         item.render(td);
12548         this.items.add(item);
12549         return item;
12550     },
12551     
12552     /**
12553      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12554      * @param {Object/Array} config A button config or array of configs
12555      * @return {Roo.Toolbar.Button/Array}
12556      */
12557     addButton : function(config){
12558         if(config instanceof Array){
12559             var buttons = [];
12560             for(var i = 0, len = config.length; i < len; i++) {
12561                 buttons.push(this.addButton(config[i]));
12562             }
12563             return buttons;
12564         }
12565         var b = config;
12566         if(!(config instanceof Roo.Toolbar.Button)){
12567             b = config.split ?
12568                 new Roo.Toolbar.SplitButton(config) :
12569                 new Roo.Toolbar.Button(config);
12570         }
12571         var td = this.nextBlock();
12572         b.render(td);
12573         this.items.add(b);
12574         return b;
12575     },
12576     
12577     /**
12578      * Adds text to the toolbar
12579      * @param {String} text The text to add
12580      * @return {Roo.Toolbar.Item} The element's item
12581      */
12582     addText : function(text){
12583         return this.addItem(new Roo.Toolbar.TextItem(text));
12584     },
12585     
12586     /**
12587      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12588      * @param {Number} index The index where the item is to be inserted
12589      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12590      * @return {Roo.Toolbar.Button/Item}
12591      */
12592     insertButton : function(index, item){
12593         if(item instanceof Array){
12594             var buttons = [];
12595             for(var i = 0, len = item.length; i < len; i++) {
12596                buttons.push(this.insertButton(index + i, item[i]));
12597             }
12598             return buttons;
12599         }
12600         if (!(item instanceof Roo.Toolbar.Button)){
12601            item = new Roo.Toolbar.Button(item);
12602         }
12603         var td = document.createElement("td");
12604         this.tr.insertBefore(td, this.tr.childNodes[index]);
12605         item.render(td);
12606         this.items.insert(index, item);
12607         return item;
12608     },
12609     
12610     /**
12611      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12612      * @param {Object} config
12613      * @return {Roo.Toolbar.Item} The element's item
12614      */
12615     addDom : function(config, returnEl){
12616         var td = this.nextBlock();
12617         Roo.DomHelper.overwrite(td, config);
12618         var ti = new Roo.Toolbar.Item(td.firstChild);
12619         ti.render(td);
12620         this.items.add(ti);
12621         return ti;
12622     },
12623
12624     /**
12625      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12626      * @type Roo.util.MixedCollection  
12627      */
12628     fields : false,
12629     
12630     /**
12631      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12632      * Note: the field should not have been rendered yet. For a field that has already been
12633      * rendered, use {@link #addElement}.
12634      * @param {Roo.form.Field} field
12635      * @return {Roo.ToolbarItem}
12636      */
12637      
12638       
12639     addField : function(field) {
12640         if (!this.fields) {
12641             var autoId = 0;
12642             this.fields = new Roo.util.MixedCollection(false, function(o){
12643                 return o.id || ("item" + (++autoId));
12644             });
12645
12646         }
12647         
12648         var td = this.nextBlock();
12649         field.render(td);
12650         var ti = new Roo.Toolbar.Item(td.firstChild);
12651         ti.render(td);
12652         this.items.add(ti);
12653         this.fields.add(field);
12654         return ti;
12655     },
12656     /**
12657      * Hide the toolbar
12658      * @method hide
12659      */
12660      
12661       
12662     hide : function()
12663     {
12664         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12665         this.el.child('div').hide();
12666     },
12667     /**
12668      * Show the toolbar
12669      * @method show
12670      */
12671     show : function()
12672     {
12673         this.el.child('div').show();
12674     },
12675       
12676     // private
12677     nextBlock : function(){
12678         var td = document.createElement("td");
12679         this.tr.appendChild(td);
12680         return td;
12681     },
12682
12683     // private
12684     destroy : function(){
12685         if(this.items){ // rendered?
12686             Roo.destroy.apply(Roo, this.items.items);
12687         }
12688         if(this.fields){ // rendered?
12689             Roo.destroy.apply(Roo, this.fields.items);
12690         }
12691         Roo.Element.uncache(this.el, this.tr);
12692     }
12693 };
12694
12695 /**
12696  * @class Roo.Toolbar.Item
12697  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12698  * @constructor
12699  * Creates a new Item
12700  * @param {HTMLElement} el 
12701  */
12702 Roo.Toolbar.Item = function(el){
12703     this.el = Roo.getDom(el);
12704     this.id = Roo.id(this.el);
12705     this.hidden = false;
12706 };
12707
12708 Roo.Toolbar.Item.prototype = {
12709     
12710     /**
12711      * Get this item's HTML Element
12712      * @return {HTMLElement}
12713      */
12714     getEl : function(){
12715        return this.el;  
12716     },
12717
12718     // private
12719     render : function(td){
12720         this.td = td;
12721         td.appendChild(this.el);
12722     },
12723     
12724     /**
12725      * Removes and destroys this item.
12726      */
12727     destroy : function(){
12728         this.td.parentNode.removeChild(this.td);
12729     },
12730     
12731     /**
12732      * Shows this item.
12733      */
12734     show: function(){
12735         this.hidden = false;
12736         this.td.style.display = "";
12737     },
12738     
12739     /**
12740      * Hides this item.
12741      */
12742     hide: function(){
12743         this.hidden = true;
12744         this.td.style.display = "none";
12745     },
12746     
12747     /**
12748      * Convenience function for boolean show/hide.
12749      * @param {Boolean} visible true to show/false to hide
12750      */
12751     setVisible: function(visible){
12752         if(visible) {
12753             this.show();
12754         }else{
12755             this.hide();
12756         }
12757     },
12758     
12759     /**
12760      * Try to focus this item.
12761      */
12762     focus : function(){
12763         Roo.fly(this.el).focus();
12764     },
12765     
12766     /**
12767      * Disables this item.
12768      */
12769     disable : function(){
12770         Roo.fly(this.td).addClass("x-item-disabled");
12771         this.disabled = true;
12772         this.el.disabled = true;
12773     },
12774     
12775     /**
12776      * Enables this item.
12777      */
12778     enable : function(){
12779         Roo.fly(this.td).removeClass("x-item-disabled");
12780         this.disabled = false;
12781         this.el.disabled = false;
12782     }
12783 };
12784
12785
12786 /**
12787  * @class Roo.Toolbar.Separator
12788  * @extends Roo.Toolbar.Item
12789  * A simple toolbar separator class
12790  * @constructor
12791  * Creates a new Separator
12792  */
12793 Roo.Toolbar.Separator = function(){
12794     var s = document.createElement("span");
12795     s.className = "ytb-sep";
12796     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12797 };
12798 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12799     enable:Roo.emptyFn,
12800     disable:Roo.emptyFn,
12801     focus:Roo.emptyFn
12802 });
12803
12804 /**
12805  * @class Roo.Toolbar.Spacer
12806  * @extends Roo.Toolbar.Item
12807  * A simple element that adds extra horizontal space to a toolbar.
12808  * @constructor
12809  * Creates a new Spacer
12810  */
12811 Roo.Toolbar.Spacer = function(){
12812     var s = document.createElement("div");
12813     s.className = "ytb-spacer";
12814     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12815 };
12816 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12817     enable:Roo.emptyFn,
12818     disable:Roo.emptyFn,
12819     focus:Roo.emptyFn
12820 });
12821
12822 /**
12823  * @class Roo.Toolbar.Fill
12824  * @extends Roo.Toolbar.Spacer
12825  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12826  * @constructor
12827  * Creates a new Spacer
12828  */
12829 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12830     // private
12831     render : function(td){
12832         td.style.width = '100%';
12833         Roo.Toolbar.Fill.superclass.render.call(this, td);
12834     }
12835 });
12836
12837 /**
12838  * @class Roo.Toolbar.TextItem
12839  * @extends Roo.Toolbar.Item
12840  * A simple class that renders text directly into a toolbar.
12841  * @constructor
12842  * Creates a new TextItem
12843  * @param {String} text
12844  */
12845 Roo.Toolbar.TextItem = function(text){
12846     if (typeof(text) == 'object') {
12847         text = text.text;
12848     }
12849     var s = document.createElement("span");
12850     s.className = "ytb-text";
12851     s.innerHTML = text;
12852     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12853 };
12854 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12855     enable:Roo.emptyFn,
12856     disable:Roo.emptyFn,
12857     focus:Roo.emptyFn
12858 });
12859
12860 /**
12861  * @class Roo.Toolbar.Button
12862  * @extends Roo.Button
12863  * A button that renders into a toolbar.
12864  * @constructor
12865  * Creates a new Button
12866  * @param {Object} config A standard {@link Roo.Button} config object
12867  */
12868 Roo.Toolbar.Button = function(config){
12869     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12870 };
12871 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12872     render : function(td){
12873         this.td = td;
12874         Roo.Toolbar.Button.superclass.render.call(this, td);
12875     },
12876     
12877     /**
12878      * Removes and destroys this button
12879      */
12880     destroy : function(){
12881         Roo.Toolbar.Button.superclass.destroy.call(this);
12882         this.td.parentNode.removeChild(this.td);
12883     },
12884     
12885     /**
12886      * Shows this button
12887      */
12888     show: function(){
12889         this.hidden = false;
12890         this.td.style.display = "";
12891     },
12892     
12893     /**
12894      * Hides this button
12895      */
12896     hide: function(){
12897         this.hidden = true;
12898         this.td.style.display = "none";
12899     },
12900
12901     /**
12902      * Disables this item
12903      */
12904     disable : function(){
12905         Roo.fly(this.td).addClass("x-item-disabled");
12906         this.disabled = true;
12907     },
12908
12909     /**
12910      * Enables this item
12911      */
12912     enable : function(){
12913         Roo.fly(this.td).removeClass("x-item-disabled");
12914         this.disabled = false;
12915     }
12916 });
12917 // backwards compat
12918 Roo.ToolbarButton = Roo.Toolbar.Button;
12919
12920 /**
12921  * @class Roo.Toolbar.SplitButton
12922  * @extends Roo.SplitButton
12923  * A menu button that renders into a toolbar.
12924  * @constructor
12925  * Creates a new SplitButton
12926  * @param {Object} config A standard {@link Roo.SplitButton} config object
12927  */
12928 Roo.Toolbar.SplitButton = function(config){
12929     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12930 };
12931 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12932     render : function(td){
12933         this.td = td;
12934         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12935     },
12936     
12937     /**
12938      * Removes and destroys this button
12939      */
12940     destroy : function(){
12941         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12942         this.td.parentNode.removeChild(this.td);
12943     },
12944     
12945     /**
12946      * Shows this button
12947      */
12948     show: function(){
12949         this.hidden = false;
12950         this.td.style.display = "";
12951     },
12952     
12953     /**
12954      * Hides this button
12955      */
12956     hide: function(){
12957         this.hidden = true;
12958         this.td.style.display = "none";
12959     }
12960 });
12961
12962 // backwards compat
12963 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12964  * Based on:
12965  * Ext JS Library 1.1.1
12966  * Copyright(c) 2006-2007, Ext JS, LLC.
12967  *
12968  * Originally Released Under LGPL - original licence link has changed is not relivant.
12969  *
12970  * Fork - LGPL
12971  * <script type="text/javascript">
12972  */
12973  
12974 /**
12975  * @class Roo.PagingToolbar
12976  * @extends Roo.Toolbar
12977  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12978  * @constructor
12979  * Create a new PagingToolbar
12980  * @param {Object} config The config object
12981  */
12982 Roo.PagingToolbar = function(el, ds, config)
12983 {
12984     // old args format still supported... - xtype is prefered..
12985     if (typeof(el) == 'object' && el.xtype) {
12986         // created from xtype...
12987         config = el;
12988         ds = el.dataSource;
12989         el = config.container;
12990     }
12991     var items = [];
12992     if (config.items) {
12993         items = config.items;
12994         config.items = [];
12995     }
12996     
12997     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12998     this.ds = ds;
12999     this.cursor = 0;
13000     this.renderButtons(this.el);
13001     this.bind(ds);
13002     
13003     // supprot items array.
13004    
13005     Roo.each(items, function(e) {
13006         this.add(Roo.factory(e));
13007     },this);
13008     
13009 };
13010
13011 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13012     /**
13013      * @cfg {Roo.data.Store} dataSource
13014      * The underlying data store providing the paged data
13015      */
13016     /**
13017      * @cfg {String/HTMLElement/Element} container
13018      * container The id or element that will contain the toolbar
13019      */
13020     /**
13021      * @cfg {Boolean} displayInfo
13022      * True to display the displayMsg (defaults to false)
13023      */
13024     /**
13025      * @cfg {Number} pageSize
13026      * The number of records to display per page (defaults to 20)
13027      */
13028     pageSize: 20,
13029     /**
13030      * @cfg {String} displayMsg
13031      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13032      */
13033     displayMsg : 'Displaying {0} - {1} of {2}',
13034     /**
13035      * @cfg {String} emptyMsg
13036      * The message to display when no records are found (defaults to "No data to display")
13037      */
13038     emptyMsg : 'No data to display',
13039     /**
13040      * Customizable piece of the default paging text (defaults to "Page")
13041      * @type String
13042      */
13043     beforePageText : "Page",
13044     /**
13045      * Customizable piece of the default paging text (defaults to "of %0")
13046      * @type String
13047      */
13048     afterPageText : "of {0}",
13049     /**
13050      * Customizable piece of the default paging text (defaults to "First Page")
13051      * @type String
13052      */
13053     firstText : "First Page",
13054     /**
13055      * Customizable piece of the default paging text (defaults to "Previous Page")
13056      * @type String
13057      */
13058     prevText : "Previous Page",
13059     /**
13060      * Customizable piece of the default paging text (defaults to "Next Page")
13061      * @type String
13062      */
13063     nextText : "Next Page",
13064     /**
13065      * Customizable piece of the default paging text (defaults to "Last Page")
13066      * @type String
13067      */
13068     lastText : "Last Page",
13069     /**
13070      * Customizable piece of the default paging text (defaults to "Refresh")
13071      * @type String
13072      */
13073     refreshText : "Refresh",
13074
13075     // private
13076     renderButtons : function(el){
13077         Roo.PagingToolbar.superclass.render.call(this, el);
13078         this.first = this.addButton({
13079             tooltip: this.firstText,
13080             cls: "x-btn-icon x-grid-page-first",
13081             disabled: true,
13082             handler: this.onClick.createDelegate(this, ["first"])
13083         });
13084         this.prev = this.addButton({
13085             tooltip: this.prevText,
13086             cls: "x-btn-icon x-grid-page-prev",
13087             disabled: true,
13088             handler: this.onClick.createDelegate(this, ["prev"])
13089         });
13090         //this.addSeparator();
13091         this.add(this.beforePageText);
13092         this.field = Roo.get(this.addDom({
13093            tag: "input",
13094            type: "text",
13095            size: "3",
13096            value: "1",
13097            cls: "x-grid-page-number"
13098         }).el);
13099         this.field.on("keydown", this.onPagingKeydown, this);
13100         this.field.on("focus", function(){this.dom.select();});
13101         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13102         this.field.setHeight(18);
13103         //this.addSeparator();
13104         this.next = this.addButton({
13105             tooltip: this.nextText,
13106             cls: "x-btn-icon x-grid-page-next",
13107             disabled: true,
13108             handler: this.onClick.createDelegate(this, ["next"])
13109         });
13110         this.last = this.addButton({
13111             tooltip: this.lastText,
13112             cls: "x-btn-icon x-grid-page-last",
13113             disabled: true,
13114             handler: this.onClick.createDelegate(this, ["last"])
13115         });
13116         //this.addSeparator();
13117         this.loading = this.addButton({
13118             tooltip: this.refreshText,
13119             cls: "x-btn-icon x-grid-loading",
13120             handler: this.onClick.createDelegate(this, ["refresh"])
13121         });
13122
13123         if(this.displayInfo){
13124             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13125         }
13126     },
13127
13128     // private
13129     updateInfo : function(){
13130         if(this.displayEl){
13131             var count = this.ds.getCount();
13132             var msg = count == 0 ?
13133                 this.emptyMsg :
13134                 String.format(
13135                     this.displayMsg,
13136                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13137                 );
13138             this.displayEl.update(msg);
13139         }
13140     },
13141
13142     // private
13143     onLoad : function(ds, r, o){
13144        this.cursor = o.params ? o.params.start : 0;
13145        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13146
13147        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13148        this.field.dom.value = ap;
13149        this.first.setDisabled(ap == 1);
13150        this.prev.setDisabled(ap == 1);
13151        this.next.setDisabled(ap == ps);
13152        this.last.setDisabled(ap == ps);
13153        this.loading.enable();
13154        this.updateInfo();
13155     },
13156
13157     // private
13158     getPageData : function(){
13159         var total = this.ds.getTotalCount();
13160         return {
13161             total : total,
13162             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13163             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13164         };
13165     },
13166
13167     // private
13168     onLoadError : function(){
13169         this.loading.enable();
13170     },
13171
13172     // private
13173     onPagingKeydown : function(e){
13174         var k = e.getKey();
13175         var d = this.getPageData();
13176         if(k == e.RETURN){
13177             var v = this.field.dom.value, pageNum;
13178             if(!v || isNaN(pageNum = parseInt(v, 10))){
13179                 this.field.dom.value = d.activePage;
13180                 return;
13181             }
13182             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13183             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13184             e.stopEvent();
13185         }
13186         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))
13187         {
13188           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13189           this.field.dom.value = pageNum;
13190           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13191           e.stopEvent();
13192         }
13193         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13194         {
13195           var v = this.field.dom.value, pageNum; 
13196           var increment = (e.shiftKey) ? 10 : 1;
13197           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13198             increment *= -1;
13199           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13200             this.field.dom.value = d.activePage;
13201             return;
13202           }
13203           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13204           {
13205             this.field.dom.value = parseInt(v, 10) + increment;
13206             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13207             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13208           }
13209           e.stopEvent();
13210         }
13211     },
13212
13213     // private
13214     beforeLoad : function(){
13215         if(this.loading){
13216             this.loading.disable();
13217         }
13218     },
13219
13220     // private
13221     onClick : function(which){
13222         var ds = this.ds;
13223         switch(which){
13224             case "first":
13225                 ds.load({params:{start: 0, limit: this.pageSize}});
13226             break;
13227             case "prev":
13228                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13229             break;
13230             case "next":
13231                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13232             break;
13233             case "last":
13234                 var total = ds.getTotalCount();
13235                 var extra = total % this.pageSize;
13236                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13237                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13238             break;
13239             case "refresh":
13240                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13241             break;
13242         }
13243     },
13244
13245     /**
13246      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13247      * @param {Roo.data.Store} store The data store to unbind
13248      */
13249     unbind : function(ds){
13250         ds.un("beforeload", this.beforeLoad, this);
13251         ds.un("load", this.onLoad, this);
13252         ds.un("loadexception", this.onLoadError, this);
13253         ds.un("remove", this.updateInfo, this);
13254         ds.un("add", this.updateInfo, this);
13255         this.ds = undefined;
13256     },
13257
13258     /**
13259      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13260      * @param {Roo.data.Store} store The data store to bind
13261      */
13262     bind : function(ds){
13263         ds.on("beforeload", this.beforeLoad, this);
13264         ds.on("load", this.onLoad, this);
13265         ds.on("loadexception", this.onLoadError, this);
13266         ds.on("remove", this.updateInfo, this);
13267         ds.on("add", this.updateInfo, this);
13268         this.ds = ds;
13269     }
13270 });/*
13271  * Based on:
13272  * Ext JS Library 1.1.1
13273  * Copyright(c) 2006-2007, Ext JS, LLC.
13274  *
13275  * Originally Released Under LGPL - original licence link has changed is not relivant.
13276  *
13277  * Fork - LGPL
13278  * <script type="text/javascript">
13279  */
13280
13281 /**
13282  * @class Roo.Resizable
13283  * @extends Roo.util.Observable
13284  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13285  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13286  * 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
13287  * the element will be wrapped for you automatically.</p>
13288  * <p>Here is the list of valid resize handles:</p>
13289  * <pre>
13290 Value   Description
13291 ------  -------------------
13292  'n'     north
13293  's'     south
13294  'e'     east
13295  'w'     west
13296  'nw'    northwest
13297  'sw'    southwest
13298  'se'    southeast
13299  'ne'    northeast
13300  'hd'    horizontal drag
13301  'all'   all
13302 </pre>
13303  * <p>Here's an example showing the creation of a typical Resizable:</p>
13304  * <pre><code>
13305 var resizer = new Roo.Resizable("element-id", {
13306     handles: 'all',
13307     minWidth: 200,
13308     minHeight: 100,
13309     maxWidth: 500,
13310     maxHeight: 400,
13311     pinned: true
13312 });
13313 resizer.on("resize", myHandler);
13314 </code></pre>
13315  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13316  * resizer.east.setDisplayed(false);</p>
13317  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13318  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13319  * resize operation's new size (defaults to [0, 0])
13320  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13321  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13322  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13323  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13324  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13325  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13326  * @cfg {Number} width The width of the element in pixels (defaults to null)
13327  * @cfg {Number} height The height of the element in pixels (defaults to null)
13328  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13329  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13330  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13331  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13332  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13333  * in favor of the handles config option (defaults to false)
13334  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13335  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13336  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13337  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13338  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13339  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13340  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13341  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13342  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13343  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13344  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13345  * @constructor
13346  * Create a new resizable component
13347  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13348  * @param {Object} config configuration options
13349   */
13350 Roo.Resizable = function(el, config)
13351 {
13352     this.el = Roo.get(el);
13353
13354     if(config && config.wrap){
13355         config.resizeChild = this.el;
13356         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13357         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13358         this.el.setStyle("overflow", "hidden");
13359         this.el.setPositioning(config.resizeChild.getPositioning());
13360         config.resizeChild.clearPositioning();
13361         if(!config.width || !config.height){
13362             var csize = config.resizeChild.getSize();
13363             this.el.setSize(csize.width, csize.height);
13364         }
13365         if(config.pinned && !config.adjustments){
13366             config.adjustments = "auto";
13367         }
13368     }
13369
13370     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13371     this.proxy.unselectable();
13372     this.proxy.enableDisplayMode('block');
13373
13374     Roo.apply(this, config);
13375
13376     if(this.pinned){
13377         this.disableTrackOver = true;
13378         this.el.addClass("x-resizable-pinned");
13379     }
13380     // if the element isn't positioned, make it relative
13381     var position = this.el.getStyle("position");
13382     if(position != "absolute" && position != "fixed"){
13383         this.el.setStyle("position", "relative");
13384     }
13385     if(!this.handles){ // no handles passed, must be legacy style
13386         this.handles = 's,e,se';
13387         if(this.multiDirectional){
13388             this.handles += ',n,w';
13389         }
13390     }
13391     if(this.handles == "all"){
13392         this.handles = "n s e w ne nw se sw";
13393     }
13394     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13395     var ps = Roo.Resizable.positions;
13396     for(var i = 0, len = hs.length; i < len; i++){
13397         if(hs[i] && ps[hs[i]]){
13398             var pos = ps[hs[i]];
13399             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13400         }
13401     }
13402     // legacy
13403     this.corner = this.southeast;
13404     
13405     // updateBox = the box can move..
13406     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13407         this.updateBox = true;
13408     }
13409
13410     this.activeHandle = null;
13411
13412     if(this.resizeChild){
13413         if(typeof this.resizeChild == "boolean"){
13414             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13415         }else{
13416             this.resizeChild = Roo.get(this.resizeChild, true);
13417         }
13418     }
13419     
13420     if(this.adjustments == "auto"){
13421         var rc = this.resizeChild;
13422         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13423         if(rc && (hw || hn)){
13424             rc.position("relative");
13425             rc.setLeft(hw ? hw.el.getWidth() : 0);
13426             rc.setTop(hn ? hn.el.getHeight() : 0);
13427         }
13428         this.adjustments = [
13429             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13430             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13431         ];
13432     }
13433
13434     if(this.draggable){
13435         this.dd = this.dynamic ?
13436             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13437         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13438     }
13439
13440     // public events
13441     this.addEvents({
13442         /**
13443          * @event beforeresize
13444          * Fired before resize is allowed. Set enabled to false to cancel resize.
13445          * @param {Roo.Resizable} this
13446          * @param {Roo.EventObject} e The mousedown event
13447          */
13448         "beforeresize" : true,
13449         /**
13450          * @event resize
13451          * Fired after a resize.
13452          * @param {Roo.Resizable} this
13453          * @param {Number} width The new width
13454          * @param {Number} height The new height
13455          * @param {Roo.EventObject} e The mouseup event
13456          */
13457         "resize" : true
13458     });
13459
13460     if(this.width !== null && this.height !== null){
13461         this.resizeTo(this.width, this.height);
13462     }else{
13463         this.updateChildSize();
13464     }
13465     if(Roo.isIE){
13466         this.el.dom.style.zoom = 1;
13467     }
13468     Roo.Resizable.superclass.constructor.call(this);
13469 };
13470
13471 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13472         resizeChild : false,
13473         adjustments : [0, 0],
13474         minWidth : 5,
13475         minHeight : 5,
13476         maxWidth : 10000,
13477         maxHeight : 10000,
13478         enabled : true,
13479         animate : false,
13480         duration : .35,
13481         dynamic : false,
13482         handles : false,
13483         multiDirectional : false,
13484         disableTrackOver : false,
13485         easing : 'easeOutStrong',
13486         widthIncrement : 0,
13487         heightIncrement : 0,
13488         pinned : false,
13489         width : null,
13490         height : null,
13491         preserveRatio : false,
13492         transparent: false,
13493         minX: 0,
13494         minY: 0,
13495         draggable: false,
13496
13497         /**
13498          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13499          */
13500         constrainTo: undefined,
13501         /**
13502          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13503          */
13504         resizeRegion: undefined,
13505
13506
13507     /**
13508      * Perform a manual resize
13509      * @param {Number} width
13510      * @param {Number} height
13511      */
13512     resizeTo : function(width, height){
13513         this.el.setSize(width, height);
13514         this.updateChildSize();
13515         this.fireEvent("resize", this, width, height, null);
13516     },
13517
13518     // private
13519     startSizing : function(e, handle){
13520         this.fireEvent("beforeresize", this, e);
13521         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13522
13523             if(!this.overlay){
13524                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13525                 this.overlay.unselectable();
13526                 this.overlay.enableDisplayMode("block");
13527                 this.overlay.on("mousemove", this.onMouseMove, this);
13528                 this.overlay.on("mouseup", this.onMouseUp, this);
13529             }
13530             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13531
13532             this.resizing = true;
13533             this.startBox = this.el.getBox();
13534             this.startPoint = e.getXY();
13535             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13536                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13537
13538             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13539             this.overlay.show();
13540
13541             if(this.constrainTo) {
13542                 var ct = Roo.get(this.constrainTo);
13543                 this.resizeRegion = ct.getRegion().adjust(
13544                     ct.getFrameWidth('t'),
13545                     ct.getFrameWidth('l'),
13546                     -ct.getFrameWidth('b'),
13547                     -ct.getFrameWidth('r')
13548                 );
13549             }
13550
13551             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13552             this.proxy.show();
13553             this.proxy.setBox(this.startBox);
13554             if(!this.dynamic){
13555                 this.proxy.setStyle('visibility', 'visible');
13556             }
13557         }
13558     },
13559
13560     // private
13561     onMouseDown : function(handle, e){
13562         if(this.enabled){
13563             e.stopEvent();
13564             this.activeHandle = handle;
13565             this.startSizing(e, handle);
13566         }
13567     },
13568
13569     // private
13570     onMouseUp : function(e){
13571         var size = this.resizeElement();
13572         this.resizing = false;
13573         this.handleOut();
13574         this.overlay.hide();
13575         this.proxy.hide();
13576         this.fireEvent("resize", this, size.width, size.height, e);
13577     },
13578
13579     // private
13580     updateChildSize : function(){
13581         if(this.resizeChild){
13582             var el = this.el;
13583             var child = this.resizeChild;
13584             var adj = this.adjustments;
13585             if(el.dom.offsetWidth){
13586                 var b = el.getSize(true);
13587                 child.setSize(b.width+adj[0], b.height+adj[1]);
13588             }
13589             // Second call here for IE
13590             // The first call enables instant resizing and
13591             // the second call corrects scroll bars if they
13592             // exist
13593             if(Roo.isIE){
13594                 setTimeout(function(){
13595                     if(el.dom.offsetWidth){
13596                         var b = el.getSize(true);
13597                         child.setSize(b.width+adj[0], b.height+adj[1]);
13598                     }
13599                 }, 10);
13600             }
13601         }
13602     },
13603
13604     // private
13605     snap : function(value, inc, min){
13606         if(!inc || !value) return value;
13607         var newValue = value;
13608         var m = value % inc;
13609         if(m > 0){
13610             if(m > (inc/2)){
13611                 newValue = value + (inc-m);
13612             }else{
13613                 newValue = value - m;
13614             }
13615         }
13616         return Math.max(min, newValue);
13617     },
13618
13619     // private
13620     resizeElement : function(){
13621         var box = this.proxy.getBox();
13622         if(this.updateBox){
13623             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13624         }else{
13625             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13626         }
13627         this.updateChildSize();
13628         if(!this.dynamic){
13629             this.proxy.hide();
13630         }
13631         return box;
13632     },
13633
13634     // private
13635     constrain : function(v, diff, m, mx){
13636         if(v - diff < m){
13637             diff = v - m;
13638         }else if(v - diff > mx){
13639             diff = mx - v;
13640         }
13641         return diff;
13642     },
13643
13644     // private
13645     onMouseMove : function(e){
13646         if(this.enabled){
13647             try{// try catch so if something goes wrong the user doesn't get hung
13648
13649             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13650                 return;
13651             }
13652
13653             //var curXY = this.startPoint;
13654             var curSize = this.curSize || this.startBox;
13655             var x = this.startBox.x, y = this.startBox.y;
13656             var ox = x, oy = y;
13657             var w = curSize.width, h = curSize.height;
13658             var ow = w, oh = h;
13659             var mw = this.minWidth, mh = this.minHeight;
13660             var mxw = this.maxWidth, mxh = this.maxHeight;
13661             var wi = this.widthIncrement;
13662             var hi = this.heightIncrement;
13663
13664             var eventXY = e.getXY();
13665             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13666             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13667
13668             var pos = this.activeHandle.position;
13669
13670             switch(pos){
13671                 case "east":
13672                     w += diffX;
13673                     w = Math.min(Math.max(mw, w), mxw);
13674                     break;
13675              
13676                 case "south":
13677                     h += diffY;
13678                     h = Math.min(Math.max(mh, h), mxh);
13679                     break;
13680                 case "southeast":
13681                     w += diffX;
13682                     h += diffY;
13683                     w = Math.min(Math.max(mw, w), mxw);
13684                     h = Math.min(Math.max(mh, h), mxh);
13685                     break;
13686                 case "north":
13687                     diffY = this.constrain(h, diffY, mh, mxh);
13688                     y += diffY;
13689                     h -= diffY;
13690                     break;
13691                 case "hdrag":
13692                     
13693                     if (wi) {
13694                         var adiffX = Math.abs(diffX);
13695                         var sub = (adiffX % wi); // how much 
13696                         if (sub > (wi/2)) { // far enough to snap
13697                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13698                         } else {
13699                             // remove difference.. 
13700                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13701                         }
13702                     }
13703                     x += diffX;
13704                     x = Math.max(this.minX, x);
13705                     break;
13706                 case "west":
13707                     diffX = this.constrain(w, diffX, mw, mxw);
13708                     x += diffX;
13709                     w -= diffX;
13710                     break;
13711                 case "northeast":
13712                     w += diffX;
13713                     w = Math.min(Math.max(mw, w), mxw);
13714                     diffY = this.constrain(h, diffY, mh, mxh);
13715                     y += diffY;
13716                     h -= diffY;
13717                     break;
13718                 case "northwest":
13719                     diffX = this.constrain(w, diffX, mw, mxw);
13720                     diffY = this.constrain(h, diffY, mh, mxh);
13721                     y += diffY;
13722                     h -= diffY;
13723                     x += diffX;
13724                     w -= diffX;
13725                     break;
13726                case "southwest":
13727                     diffX = this.constrain(w, diffX, mw, mxw);
13728                     h += diffY;
13729                     h = Math.min(Math.max(mh, h), mxh);
13730                     x += diffX;
13731                     w -= diffX;
13732                     break;
13733             }
13734
13735             var sw = this.snap(w, wi, mw);
13736             var sh = this.snap(h, hi, mh);
13737             if(sw != w || sh != h){
13738                 switch(pos){
13739                     case "northeast":
13740                         y -= sh - h;
13741                     break;
13742                     case "north":
13743                         y -= sh - h;
13744                         break;
13745                     case "southwest":
13746                         x -= sw - w;
13747                     break;
13748                     case "west":
13749                         x -= sw - w;
13750                         break;
13751                     case "northwest":
13752                         x -= sw - w;
13753                         y -= sh - h;
13754                     break;
13755                 }
13756                 w = sw;
13757                 h = sh;
13758             }
13759
13760             if(this.preserveRatio){
13761                 switch(pos){
13762                     case "southeast":
13763                     case "east":
13764                         h = oh * (w/ow);
13765                         h = Math.min(Math.max(mh, h), mxh);
13766                         w = ow * (h/oh);
13767                        break;
13768                     case "south":
13769                         w = ow * (h/oh);
13770                         w = Math.min(Math.max(mw, w), mxw);
13771                         h = oh * (w/ow);
13772                         break;
13773                     case "northeast":
13774                         w = ow * (h/oh);
13775                         w = Math.min(Math.max(mw, w), mxw);
13776                         h = oh * (w/ow);
13777                     break;
13778                     case "north":
13779                         var tw = w;
13780                         w = ow * (h/oh);
13781                         w = Math.min(Math.max(mw, w), mxw);
13782                         h = oh * (w/ow);
13783                         x += (tw - w) / 2;
13784                         break;
13785                     case "southwest":
13786                         h = oh * (w/ow);
13787                         h = Math.min(Math.max(mh, h), mxh);
13788                         var tw = w;
13789                         w = ow * (h/oh);
13790                         x += tw - w;
13791                         break;
13792                     case "west":
13793                         var th = h;
13794                         h = oh * (w/ow);
13795                         h = Math.min(Math.max(mh, h), mxh);
13796                         y += (th - h) / 2;
13797                         var tw = w;
13798                         w = ow * (h/oh);
13799                         x += tw - w;
13800                        break;
13801                     case "northwest":
13802                         var tw = w;
13803                         var th = h;
13804                         h = oh * (w/ow);
13805                         h = Math.min(Math.max(mh, h), mxh);
13806                         w = ow * (h/oh);
13807                         y += th - h;
13808                         x += tw - w;
13809                        break;
13810
13811                 }
13812             }
13813             if (pos == 'hdrag') {
13814                 w = ow;
13815             }
13816             this.proxy.setBounds(x, y, w, h);
13817             if(this.dynamic){
13818                 this.resizeElement();
13819             }
13820             }catch(e){}
13821         }
13822     },
13823
13824     // private
13825     handleOver : function(){
13826         if(this.enabled){
13827             this.el.addClass("x-resizable-over");
13828         }
13829     },
13830
13831     // private
13832     handleOut : function(){
13833         if(!this.resizing){
13834             this.el.removeClass("x-resizable-over");
13835         }
13836     },
13837
13838     /**
13839      * Returns the element this component is bound to.
13840      * @return {Roo.Element}
13841      */
13842     getEl : function(){
13843         return this.el;
13844     },
13845
13846     /**
13847      * Returns the resizeChild element (or null).
13848      * @return {Roo.Element}
13849      */
13850     getResizeChild : function(){
13851         return this.resizeChild;
13852     },
13853
13854     /**
13855      * Destroys this resizable. If the element was wrapped and
13856      * removeEl is not true then the element remains.
13857      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13858      */
13859     destroy : function(removeEl){
13860         this.proxy.remove();
13861         if(this.overlay){
13862             this.overlay.removeAllListeners();
13863             this.overlay.remove();
13864         }
13865         var ps = Roo.Resizable.positions;
13866         for(var k in ps){
13867             if(typeof ps[k] != "function" && this[ps[k]]){
13868                 var h = this[ps[k]];
13869                 h.el.removeAllListeners();
13870                 h.el.remove();
13871             }
13872         }
13873         if(removeEl){
13874             this.el.update("");
13875             this.el.remove();
13876         }
13877     }
13878 });
13879
13880 // private
13881 // hash to map config positions to true positions
13882 Roo.Resizable.positions = {
13883     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13884     hd: "hdrag"
13885 };
13886
13887 // private
13888 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13889     if(!this.tpl){
13890         // only initialize the template if resizable is used
13891         var tpl = Roo.DomHelper.createTemplate(
13892             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13893         );
13894         tpl.compile();
13895         Roo.Resizable.Handle.prototype.tpl = tpl;
13896     }
13897     this.position = pos;
13898     this.rz = rz;
13899     // show north drag fro topdra
13900     var handlepos = pos == 'hdrag' ? 'north' : pos;
13901     
13902     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13903     if (pos == 'hdrag') {
13904         this.el.setStyle('cursor', 'pointer');
13905     }
13906     this.el.unselectable();
13907     if(transparent){
13908         this.el.setOpacity(0);
13909     }
13910     this.el.on("mousedown", this.onMouseDown, this);
13911     if(!disableTrackOver){
13912         this.el.on("mouseover", this.onMouseOver, this);
13913         this.el.on("mouseout", this.onMouseOut, this);
13914     }
13915 };
13916
13917 // private
13918 Roo.Resizable.Handle.prototype = {
13919     afterResize : function(rz){
13920         // do nothing
13921     },
13922     // private
13923     onMouseDown : function(e){
13924         this.rz.onMouseDown(this, e);
13925     },
13926     // private
13927     onMouseOver : function(e){
13928         this.rz.handleOver(this, e);
13929     },
13930     // private
13931     onMouseOut : function(e){
13932         this.rz.handleOut(this, e);
13933     }
13934 };/*
13935  * Based on:
13936  * Ext JS Library 1.1.1
13937  * Copyright(c) 2006-2007, Ext JS, LLC.
13938  *
13939  * Originally Released Under LGPL - original licence link has changed is not relivant.
13940  *
13941  * Fork - LGPL
13942  * <script type="text/javascript">
13943  */
13944
13945 /**
13946  * @class Roo.Editor
13947  * @extends Roo.Component
13948  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13949  * @constructor
13950  * Create a new Editor
13951  * @param {Roo.form.Field} field The Field object (or descendant)
13952  * @param {Object} config The config object
13953  */
13954 Roo.Editor = function(field, config){
13955     Roo.Editor.superclass.constructor.call(this, config);
13956     this.field = field;
13957     this.addEvents({
13958         /**
13959              * @event beforestartedit
13960              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13961              * false from the handler of this event.
13962              * @param {Editor} this
13963              * @param {Roo.Element} boundEl The underlying element bound to this editor
13964              * @param {Mixed} value The field value being set
13965              */
13966         "beforestartedit" : true,
13967         /**
13968              * @event startedit
13969              * Fires when this editor is displayed
13970              * @param {Roo.Element} boundEl The underlying element bound to this editor
13971              * @param {Mixed} value The starting field value
13972              */
13973         "startedit" : true,
13974         /**
13975              * @event beforecomplete
13976              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13977              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13978              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13979              * event will not fire since no edit actually occurred.
13980              * @param {Editor} this
13981              * @param {Mixed} value The current field value
13982              * @param {Mixed} startValue The original field value
13983              */
13984         "beforecomplete" : true,
13985         /**
13986              * @event complete
13987              * Fires after editing is complete and any changed value has been written to the underlying field.
13988              * @param {Editor} this
13989              * @param {Mixed} value The current field value
13990              * @param {Mixed} startValue The original field value
13991              */
13992         "complete" : true,
13993         /**
13994          * @event specialkey
13995          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13996          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13997          * @param {Roo.form.Field} this
13998          * @param {Roo.EventObject} e The event object
13999          */
14000         "specialkey" : true
14001     });
14002 };
14003
14004 Roo.extend(Roo.Editor, Roo.Component, {
14005     /**
14006      * @cfg {Boolean/String} autosize
14007      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14008      * or "height" to adopt the height only (defaults to false)
14009      */
14010     /**
14011      * @cfg {Boolean} revertInvalid
14012      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14013      * validation fails (defaults to true)
14014      */
14015     /**
14016      * @cfg {Boolean} ignoreNoChange
14017      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14018      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14019      * will never be ignored.
14020      */
14021     /**
14022      * @cfg {Boolean} hideEl
14023      * False to keep the bound element visible while the editor is displayed (defaults to true)
14024      */
14025     /**
14026      * @cfg {Mixed} value
14027      * The data value of the underlying field (defaults to "")
14028      */
14029     value : "",
14030     /**
14031      * @cfg {String} alignment
14032      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14033      */
14034     alignment: "c-c?",
14035     /**
14036      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14037      * for bottom-right shadow (defaults to "frame")
14038      */
14039     shadow : "frame",
14040     /**
14041      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14042      */
14043     constrain : false,
14044     /**
14045      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14046      */
14047     completeOnEnter : false,
14048     /**
14049      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14050      */
14051     cancelOnEsc : false,
14052     /**
14053      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14054      */
14055     updateEl : false,
14056
14057     // private
14058     onRender : function(ct, position){
14059         this.el = new Roo.Layer({
14060             shadow: this.shadow,
14061             cls: "x-editor",
14062             parentEl : ct,
14063             shim : this.shim,
14064             shadowOffset:4,
14065             id: this.id,
14066             constrain: this.constrain
14067         });
14068         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14069         if(this.field.msgTarget != 'title'){
14070             this.field.msgTarget = 'qtip';
14071         }
14072         this.field.render(this.el);
14073         if(Roo.isGecko){
14074             this.field.el.dom.setAttribute('autocomplete', 'off');
14075         }
14076         this.field.on("specialkey", this.onSpecialKey, this);
14077         if(this.swallowKeys){
14078             this.field.el.swallowEvent(['keydown','keypress']);
14079         }
14080         this.field.show();
14081         this.field.on("blur", this.onBlur, this);
14082         if(this.field.grow){
14083             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14084         }
14085     },
14086
14087     onSpecialKey : function(field, e)
14088     {
14089         //Roo.log('editor onSpecialKey');
14090         if(this.completeOnEnter && e.getKey() == e.ENTER){
14091             e.stopEvent();
14092             this.completeEdit();
14093             return;
14094         }
14095         // do not fire special key otherwise it might hide close the editor...
14096         if(e.getKey() == e.ENTER){    
14097             return;
14098         }
14099         if(this.cancelOnEsc && e.getKey() == e.ESC){
14100             this.cancelEdit();
14101             return;
14102         } 
14103         this.fireEvent('specialkey', field, e);
14104     
14105     },
14106
14107     /**
14108      * Starts the editing process and shows the editor.
14109      * @param {String/HTMLElement/Element} el The element to edit
14110      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14111       * to the innerHTML of el.
14112      */
14113     startEdit : function(el, value){
14114         if(this.editing){
14115             this.completeEdit();
14116         }
14117         this.boundEl = Roo.get(el);
14118         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14119         if(!this.rendered){
14120             this.render(this.parentEl || document.body);
14121         }
14122         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14123             return;
14124         }
14125         this.startValue = v;
14126         this.field.setValue(v);
14127         if(this.autoSize){
14128             var sz = this.boundEl.getSize();
14129             switch(this.autoSize){
14130                 case "width":
14131                 this.setSize(sz.width,  "");
14132                 break;
14133                 case "height":
14134                 this.setSize("",  sz.height);
14135                 break;
14136                 default:
14137                 this.setSize(sz.width,  sz.height);
14138             }
14139         }
14140         this.el.alignTo(this.boundEl, this.alignment);
14141         this.editing = true;
14142         if(Roo.QuickTips){
14143             Roo.QuickTips.disable();
14144         }
14145         this.show();
14146     },
14147
14148     /**
14149      * Sets the height and width of this editor.
14150      * @param {Number} width The new width
14151      * @param {Number} height The new height
14152      */
14153     setSize : function(w, h){
14154         this.field.setSize(w, h);
14155         if(this.el){
14156             this.el.sync();
14157         }
14158     },
14159
14160     /**
14161      * Realigns the editor to the bound field based on the current alignment config value.
14162      */
14163     realign : function(){
14164         this.el.alignTo(this.boundEl, this.alignment);
14165     },
14166
14167     /**
14168      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14169      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14170      */
14171     completeEdit : function(remainVisible){
14172         if(!this.editing){
14173             return;
14174         }
14175         var v = this.getValue();
14176         if(this.revertInvalid !== false && !this.field.isValid()){
14177             v = this.startValue;
14178             this.cancelEdit(true);
14179         }
14180         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14181             this.editing = false;
14182             this.hide();
14183             return;
14184         }
14185         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14186             this.editing = false;
14187             if(this.updateEl && this.boundEl){
14188                 this.boundEl.update(v);
14189             }
14190             if(remainVisible !== true){
14191                 this.hide();
14192             }
14193             this.fireEvent("complete", this, v, this.startValue);
14194         }
14195     },
14196
14197     // private
14198     onShow : function(){
14199         this.el.show();
14200         if(this.hideEl !== false){
14201             this.boundEl.hide();
14202         }
14203         this.field.show();
14204         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14205             this.fixIEFocus = true;
14206             this.deferredFocus.defer(50, this);
14207         }else{
14208             this.field.focus();
14209         }
14210         this.fireEvent("startedit", this.boundEl, this.startValue);
14211     },
14212
14213     deferredFocus : function(){
14214         if(this.editing){
14215             this.field.focus();
14216         }
14217     },
14218
14219     /**
14220      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14221      * reverted to the original starting value.
14222      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14223      * cancel (defaults to false)
14224      */
14225     cancelEdit : function(remainVisible){
14226         if(this.editing){
14227             this.setValue(this.startValue);
14228             if(remainVisible !== true){
14229                 this.hide();
14230             }
14231         }
14232     },
14233
14234     // private
14235     onBlur : function(){
14236         if(this.allowBlur !== true && this.editing){
14237             this.completeEdit();
14238         }
14239     },
14240
14241     // private
14242     onHide : function(){
14243         if(this.editing){
14244             this.completeEdit();
14245             return;
14246         }
14247         this.field.blur();
14248         if(this.field.collapse){
14249             this.field.collapse();
14250         }
14251         this.el.hide();
14252         if(this.hideEl !== false){
14253             this.boundEl.show();
14254         }
14255         if(Roo.QuickTips){
14256             Roo.QuickTips.enable();
14257         }
14258     },
14259
14260     /**
14261      * Sets the data value of the editor
14262      * @param {Mixed} value Any valid value supported by the underlying field
14263      */
14264     setValue : function(v){
14265         this.field.setValue(v);
14266     },
14267
14268     /**
14269      * Gets the data value of the editor
14270      * @return {Mixed} The data value
14271      */
14272     getValue : function(){
14273         return this.field.getValue();
14274     }
14275 });/*
14276  * Based on:
14277  * Ext JS Library 1.1.1
14278  * Copyright(c) 2006-2007, Ext JS, LLC.
14279  *
14280  * Originally Released Under LGPL - original licence link has changed is not relivant.
14281  *
14282  * Fork - LGPL
14283  * <script type="text/javascript">
14284  */
14285  
14286 /**
14287  * @class Roo.BasicDialog
14288  * @extends Roo.util.Observable
14289  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14290  * <pre><code>
14291 var dlg = new Roo.BasicDialog("my-dlg", {
14292     height: 200,
14293     width: 300,
14294     minHeight: 100,
14295     minWidth: 150,
14296     modal: true,
14297     proxyDrag: true,
14298     shadow: true
14299 });
14300 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14301 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14302 dlg.addButton('Cancel', dlg.hide, dlg);
14303 dlg.show();
14304 </code></pre>
14305   <b>A Dialog should always be a direct child of the body element.</b>
14306  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14307  * @cfg {String} title Default text to display in the title bar (defaults to null)
14308  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14309  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14310  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14311  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14312  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14313  * (defaults to null with no animation)
14314  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14315  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14316  * property for valid values (defaults to 'all')
14317  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14318  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14319  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14320  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14321  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14322  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14323  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14324  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14325  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14326  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14327  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14328  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14329  * draggable = true (defaults to false)
14330  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14331  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14332  * shadow (defaults to false)
14333  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14334  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14335  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14336  * @cfg {Array} buttons Array of buttons
14337  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14338  * @constructor
14339  * Create a new BasicDialog.
14340  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14341  * @param {Object} config Configuration options
14342  */
14343 Roo.BasicDialog = function(el, config){
14344     this.el = Roo.get(el);
14345     var dh = Roo.DomHelper;
14346     if(!this.el && config && config.autoCreate){
14347         if(typeof config.autoCreate == "object"){
14348             if(!config.autoCreate.id){
14349                 config.autoCreate.id = el;
14350             }
14351             this.el = dh.append(document.body,
14352                         config.autoCreate, true);
14353         }else{
14354             this.el = dh.append(document.body,
14355                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14356         }
14357     }
14358     el = this.el;
14359     el.setDisplayed(true);
14360     el.hide = this.hideAction;
14361     this.id = el.id;
14362     el.addClass("x-dlg");
14363
14364     Roo.apply(this, config);
14365
14366     this.proxy = el.createProxy("x-dlg-proxy");
14367     this.proxy.hide = this.hideAction;
14368     this.proxy.setOpacity(.5);
14369     this.proxy.hide();
14370
14371     if(config.width){
14372         el.setWidth(config.width);
14373     }
14374     if(config.height){
14375         el.setHeight(config.height);
14376     }
14377     this.size = el.getSize();
14378     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14379         this.xy = [config.x,config.y];
14380     }else{
14381         this.xy = el.getCenterXY(true);
14382     }
14383     /** The header element @type Roo.Element */
14384     this.header = el.child("> .x-dlg-hd");
14385     /** The body element @type Roo.Element */
14386     this.body = el.child("> .x-dlg-bd");
14387     /** The footer element @type Roo.Element */
14388     this.footer = el.child("> .x-dlg-ft");
14389
14390     if(!this.header){
14391         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14392     }
14393     if(!this.body){
14394         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14395     }
14396
14397     this.header.unselectable();
14398     if(this.title){
14399         this.header.update(this.title);
14400     }
14401     // this element allows the dialog to be focused for keyboard event
14402     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14403     this.focusEl.swallowEvent("click", true);
14404
14405     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14406
14407     // wrap the body and footer for special rendering
14408     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14409     if(this.footer){
14410         this.bwrap.dom.appendChild(this.footer.dom);
14411     }
14412
14413     this.bg = this.el.createChild({
14414         tag: "div", cls:"x-dlg-bg",
14415         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14416     });
14417     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14418
14419
14420     if(this.autoScroll !== false && !this.autoTabs){
14421         this.body.setStyle("overflow", "auto");
14422     }
14423
14424     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14425
14426     if(this.closable !== false){
14427         this.el.addClass("x-dlg-closable");
14428         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14429         this.close.on("click", this.closeClick, this);
14430         this.close.addClassOnOver("x-dlg-close-over");
14431     }
14432     if(this.collapsible !== false){
14433         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14434         this.collapseBtn.on("click", this.collapseClick, this);
14435         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14436         this.header.on("dblclick", this.collapseClick, this);
14437     }
14438     if(this.resizable !== false){
14439         this.el.addClass("x-dlg-resizable");
14440         this.resizer = new Roo.Resizable(el, {
14441             minWidth: this.minWidth || 80,
14442             minHeight:this.minHeight || 80,
14443             handles: this.resizeHandles || "all",
14444             pinned: true
14445         });
14446         this.resizer.on("beforeresize", this.beforeResize, this);
14447         this.resizer.on("resize", this.onResize, this);
14448     }
14449     if(this.draggable !== false){
14450         el.addClass("x-dlg-draggable");
14451         if (!this.proxyDrag) {
14452             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14453         }
14454         else {
14455             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14456         }
14457         dd.setHandleElId(this.header.id);
14458         dd.endDrag = this.endMove.createDelegate(this);
14459         dd.startDrag = this.startMove.createDelegate(this);
14460         dd.onDrag = this.onDrag.createDelegate(this);
14461         dd.scroll = false;
14462         this.dd = dd;
14463     }
14464     if(this.modal){
14465         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14466         this.mask.enableDisplayMode("block");
14467         this.mask.hide();
14468         this.el.addClass("x-dlg-modal");
14469     }
14470     if(this.shadow){
14471         this.shadow = new Roo.Shadow({
14472             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14473             offset : this.shadowOffset
14474         });
14475     }else{
14476         this.shadowOffset = 0;
14477     }
14478     if(Roo.useShims && this.shim !== false){
14479         this.shim = this.el.createShim();
14480         this.shim.hide = this.hideAction;
14481         this.shim.hide();
14482     }else{
14483         this.shim = false;
14484     }
14485     if(this.autoTabs){
14486         this.initTabs();
14487     }
14488     if (this.buttons) { 
14489         var bts= this.buttons;
14490         this.buttons = [];
14491         Roo.each(bts, function(b) {
14492             this.addButton(b);
14493         }, this);
14494     }
14495     
14496     
14497     this.addEvents({
14498         /**
14499          * @event keydown
14500          * Fires when a key is pressed
14501          * @param {Roo.BasicDialog} this
14502          * @param {Roo.EventObject} e
14503          */
14504         "keydown" : true,
14505         /**
14506          * @event move
14507          * Fires when this dialog is moved by the user.
14508          * @param {Roo.BasicDialog} this
14509          * @param {Number} x The new page X
14510          * @param {Number} y The new page Y
14511          */
14512         "move" : true,
14513         /**
14514          * @event resize
14515          * Fires when this dialog is resized by the user.
14516          * @param {Roo.BasicDialog} this
14517          * @param {Number} width The new width
14518          * @param {Number} height The new height
14519          */
14520         "resize" : true,
14521         /**
14522          * @event beforehide
14523          * Fires before this dialog is hidden.
14524          * @param {Roo.BasicDialog} this
14525          */
14526         "beforehide" : true,
14527         /**
14528          * @event hide
14529          * Fires when this dialog is hidden.
14530          * @param {Roo.BasicDialog} this
14531          */
14532         "hide" : true,
14533         /**
14534          * @event beforeshow
14535          * Fires before this dialog is shown.
14536          * @param {Roo.BasicDialog} this
14537          */
14538         "beforeshow" : true,
14539         /**
14540          * @event show
14541          * Fires when this dialog is shown.
14542          * @param {Roo.BasicDialog} this
14543          */
14544         "show" : true
14545     });
14546     el.on("keydown", this.onKeyDown, this);
14547     el.on("mousedown", this.toFront, this);
14548     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14549     this.el.hide();
14550     Roo.DialogManager.register(this);
14551     Roo.BasicDialog.superclass.constructor.call(this);
14552 };
14553
14554 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14555     shadowOffset: Roo.isIE ? 6 : 5,
14556     minHeight: 80,
14557     minWidth: 200,
14558     minButtonWidth: 75,
14559     defaultButton: null,
14560     buttonAlign: "right",
14561     tabTag: 'div',
14562     firstShow: true,
14563
14564     /**
14565      * Sets the dialog title text
14566      * @param {String} text The title text to display
14567      * @return {Roo.BasicDialog} this
14568      */
14569     setTitle : function(text){
14570         this.header.update(text);
14571         return this;
14572     },
14573
14574     // private
14575     closeClick : function(){
14576         this.hide();
14577     },
14578
14579     // private
14580     collapseClick : function(){
14581         this[this.collapsed ? "expand" : "collapse"]();
14582     },
14583
14584     /**
14585      * Collapses the dialog to its minimized state (only the title bar is visible).
14586      * Equivalent to the user clicking the collapse dialog button.
14587      */
14588     collapse : function(){
14589         if(!this.collapsed){
14590             this.collapsed = true;
14591             this.el.addClass("x-dlg-collapsed");
14592             this.restoreHeight = this.el.getHeight();
14593             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14594         }
14595     },
14596
14597     /**
14598      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14599      * clicking the expand dialog button.
14600      */
14601     expand : function(){
14602         if(this.collapsed){
14603             this.collapsed = false;
14604             this.el.removeClass("x-dlg-collapsed");
14605             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14606         }
14607     },
14608
14609     /**
14610      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14611      * @return {Roo.TabPanel} The tabs component
14612      */
14613     initTabs : function(){
14614         var tabs = this.getTabs();
14615         while(tabs.getTab(0)){
14616             tabs.removeTab(0);
14617         }
14618         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14619             var dom = el.dom;
14620             tabs.addTab(Roo.id(dom), dom.title);
14621             dom.title = "";
14622         });
14623         tabs.activate(0);
14624         return tabs;
14625     },
14626
14627     // private
14628     beforeResize : function(){
14629         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14630     },
14631
14632     // private
14633     onResize : function(){
14634         this.refreshSize();
14635         this.syncBodyHeight();
14636         this.adjustAssets();
14637         this.focus();
14638         this.fireEvent("resize", this, this.size.width, this.size.height);
14639     },
14640
14641     // private
14642     onKeyDown : function(e){
14643         if(this.isVisible()){
14644             this.fireEvent("keydown", this, e);
14645         }
14646     },
14647
14648     /**
14649      * Resizes the dialog.
14650      * @param {Number} width
14651      * @param {Number} height
14652      * @return {Roo.BasicDialog} this
14653      */
14654     resizeTo : function(width, height){
14655         this.el.setSize(width, height);
14656         this.size = {width: width, height: height};
14657         this.syncBodyHeight();
14658         if(this.fixedcenter){
14659             this.center();
14660         }
14661         if(this.isVisible()){
14662             this.constrainXY();
14663             this.adjustAssets();
14664         }
14665         this.fireEvent("resize", this, width, height);
14666         return this;
14667     },
14668
14669
14670     /**
14671      * Resizes the dialog to fit the specified content size.
14672      * @param {Number} width
14673      * @param {Number} height
14674      * @return {Roo.BasicDialog} this
14675      */
14676     setContentSize : function(w, h){
14677         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14678         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14679         //if(!this.el.isBorderBox()){
14680             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14681             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14682         //}
14683         if(this.tabs){
14684             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14685             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14686         }
14687         this.resizeTo(w, h);
14688         return this;
14689     },
14690
14691     /**
14692      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14693      * executed in response to a particular key being pressed while the dialog is active.
14694      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14695      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14696      * @param {Function} fn The function to call
14697      * @param {Object} scope (optional) The scope of the function
14698      * @return {Roo.BasicDialog} this
14699      */
14700     addKeyListener : function(key, fn, scope){
14701         var keyCode, shift, ctrl, alt;
14702         if(typeof key == "object" && !(key instanceof Array)){
14703             keyCode = key["key"];
14704             shift = key["shift"];
14705             ctrl = key["ctrl"];
14706             alt = key["alt"];
14707         }else{
14708             keyCode = key;
14709         }
14710         var handler = function(dlg, e){
14711             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14712                 var k = e.getKey();
14713                 if(keyCode instanceof Array){
14714                     for(var i = 0, len = keyCode.length; i < len; i++){
14715                         if(keyCode[i] == k){
14716                           fn.call(scope || window, dlg, k, e);
14717                           return;
14718                         }
14719                     }
14720                 }else{
14721                     if(k == keyCode){
14722                         fn.call(scope || window, dlg, k, e);
14723                     }
14724                 }
14725             }
14726         };
14727         this.on("keydown", handler);
14728         return this;
14729     },
14730
14731     /**
14732      * Returns the TabPanel component (creates it if it doesn't exist).
14733      * Note: If you wish to simply check for the existence of tabs without creating them,
14734      * check for a null 'tabs' property.
14735      * @return {Roo.TabPanel} The tabs component
14736      */
14737     getTabs : function(){
14738         if(!this.tabs){
14739             this.el.addClass("x-dlg-auto-tabs");
14740             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14741             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14742         }
14743         return this.tabs;
14744     },
14745
14746     /**
14747      * Adds a button to the footer section of the dialog.
14748      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14749      * object or a valid Roo.DomHelper element config
14750      * @param {Function} handler The function called when the button is clicked
14751      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14752      * @return {Roo.Button} The new button
14753      */
14754     addButton : function(config, handler, scope){
14755         var dh = Roo.DomHelper;
14756         if(!this.footer){
14757             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14758         }
14759         if(!this.btnContainer){
14760             var tb = this.footer.createChild({
14761
14762                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14763                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14764             }, null, true);
14765             this.btnContainer = tb.firstChild.firstChild.firstChild;
14766         }
14767         var bconfig = {
14768             handler: handler,
14769             scope: scope,
14770             minWidth: this.minButtonWidth,
14771             hideParent:true
14772         };
14773         if(typeof config == "string"){
14774             bconfig.text = config;
14775         }else{
14776             if(config.tag){
14777                 bconfig.dhconfig = config;
14778             }else{
14779                 Roo.apply(bconfig, config);
14780             }
14781         }
14782         var fc = false;
14783         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14784             bconfig.position = Math.max(0, bconfig.position);
14785             fc = this.btnContainer.childNodes[bconfig.position];
14786         }
14787          
14788         var btn = new Roo.Button(
14789             fc ? 
14790                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14791                 : this.btnContainer.appendChild(document.createElement("td")),
14792             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14793             bconfig
14794         );
14795         this.syncBodyHeight();
14796         if(!this.buttons){
14797             /**
14798              * Array of all the buttons that have been added to this dialog via addButton
14799              * @type Array
14800              */
14801             this.buttons = [];
14802         }
14803         this.buttons.push(btn);
14804         return btn;
14805     },
14806
14807     /**
14808      * Sets the default button to be focused when the dialog is displayed.
14809      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14810      * @return {Roo.BasicDialog} this
14811      */
14812     setDefaultButton : function(btn){
14813         this.defaultButton = btn;
14814         return this;
14815     },
14816
14817     // private
14818     getHeaderFooterHeight : function(safe){
14819         var height = 0;
14820         if(this.header){
14821            height += this.header.getHeight();
14822         }
14823         if(this.footer){
14824            var fm = this.footer.getMargins();
14825             height += (this.footer.getHeight()+fm.top+fm.bottom);
14826         }
14827         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14828         height += this.centerBg.getPadding("tb");
14829         return height;
14830     },
14831
14832     // private
14833     syncBodyHeight : function(){
14834         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14835         var height = this.size.height - this.getHeaderFooterHeight(false);
14836         bd.setHeight(height-bd.getMargins("tb"));
14837         var hh = this.header.getHeight();
14838         var h = this.size.height-hh;
14839         cb.setHeight(h);
14840         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14841         bw.setHeight(h-cb.getPadding("tb"));
14842         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14843         bd.setWidth(bw.getWidth(true));
14844         if(this.tabs){
14845             this.tabs.syncHeight();
14846             if(Roo.isIE){
14847                 this.tabs.el.repaint();
14848             }
14849         }
14850     },
14851
14852     /**
14853      * Restores the previous state of the dialog if Roo.state is configured.
14854      * @return {Roo.BasicDialog} this
14855      */
14856     restoreState : function(){
14857         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14858         if(box && box.width){
14859             this.xy = [box.x, box.y];
14860             this.resizeTo(box.width, box.height);
14861         }
14862         return this;
14863     },
14864
14865     // private
14866     beforeShow : function(){
14867         this.expand();
14868         if(this.fixedcenter){
14869             this.xy = this.el.getCenterXY(true);
14870         }
14871         if(this.modal){
14872             Roo.get(document.body).addClass("x-body-masked");
14873             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14874             this.mask.show();
14875         }
14876         this.constrainXY();
14877     },
14878
14879     // private
14880     animShow : function(){
14881         var b = Roo.get(this.animateTarget).getBox();
14882         this.proxy.setSize(b.width, b.height);
14883         this.proxy.setLocation(b.x, b.y);
14884         this.proxy.show();
14885         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14886                     true, .35, this.showEl.createDelegate(this));
14887     },
14888
14889     /**
14890      * Shows the dialog.
14891      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14892      * @return {Roo.BasicDialog} this
14893      */
14894     show : function(animateTarget){
14895         if (this.fireEvent("beforeshow", this) === false){
14896             return;
14897         }
14898         if(this.syncHeightBeforeShow){
14899             this.syncBodyHeight();
14900         }else if(this.firstShow){
14901             this.firstShow = false;
14902             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14903         }
14904         this.animateTarget = animateTarget || this.animateTarget;
14905         if(!this.el.isVisible()){
14906             this.beforeShow();
14907             if(this.animateTarget && Roo.get(this.animateTarget)){
14908                 this.animShow();
14909             }else{
14910                 this.showEl();
14911             }
14912         }
14913         return this;
14914     },
14915
14916     // private
14917     showEl : function(){
14918         this.proxy.hide();
14919         this.el.setXY(this.xy);
14920         this.el.show();
14921         this.adjustAssets(true);
14922         this.toFront();
14923         this.focus();
14924         // IE peekaboo bug - fix found by Dave Fenwick
14925         if(Roo.isIE){
14926             this.el.repaint();
14927         }
14928         this.fireEvent("show", this);
14929     },
14930
14931     /**
14932      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14933      * dialog itself will receive focus.
14934      */
14935     focus : function(){
14936         if(this.defaultButton){
14937             this.defaultButton.focus();
14938         }else{
14939             this.focusEl.focus();
14940         }
14941     },
14942
14943     // private
14944     constrainXY : function(){
14945         if(this.constraintoviewport !== false){
14946             if(!this.viewSize){
14947                 if(this.container){
14948                     var s = this.container.getSize();
14949                     this.viewSize = [s.width, s.height];
14950                 }else{
14951                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14952                 }
14953             }
14954             var s = Roo.get(this.container||document).getScroll();
14955
14956             var x = this.xy[0], y = this.xy[1];
14957             var w = this.size.width, h = this.size.height;
14958             var vw = this.viewSize[0], vh = this.viewSize[1];
14959             // only move it if it needs it
14960             var moved = false;
14961             // first validate right/bottom
14962             if(x + w > vw+s.left){
14963                 x = vw - w;
14964                 moved = true;
14965             }
14966             if(y + h > vh+s.top){
14967                 y = vh - h;
14968                 moved = true;
14969             }
14970             // then make sure top/left isn't negative
14971             if(x < s.left){
14972                 x = s.left;
14973                 moved = true;
14974             }
14975             if(y < s.top){
14976                 y = s.top;
14977                 moved = true;
14978             }
14979             if(moved){
14980                 // cache xy
14981                 this.xy = [x, y];
14982                 if(this.isVisible()){
14983                     this.el.setLocation(x, y);
14984                     this.adjustAssets();
14985                 }
14986             }
14987         }
14988     },
14989
14990     // private
14991     onDrag : function(){
14992         if(!this.proxyDrag){
14993             this.xy = this.el.getXY();
14994             this.adjustAssets();
14995         }
14996     },
14997
14998     // private
14999     adjustAssets : function(doShow){
15000         var x = this.xy[0], y = this.xy[1];
15001         var w = this.size.width, h = this.size.height;
15002         if(doShow === true){
15003             if(this.shadow){
15004                 this.shadow.show(this.el);
15005             }
15006             if(this.shim){
15007                 this.shim.show();
15008             }
15009         }
15010         if(this.shadow && this.shadow.isVisible()){
15011             this.shadow.show(this.el);
15012         }
15013         if(this.shim && this.shim.isVisible()){
15014             this.shim.setBounds(x, y, w, h);
15015         }
15016     },
15017
15018     // private
15019     adjustViewport : function(w, h){
15020         if(!w || !h){
15021             w = Roo.lib.Dom.getViewWidth();
15022             h = Roo.lib.Dom.getViewHeight();
15023         }
15024         // cache the size
15025         this.viewSize = [w, h];
15026         if(this.modal && this.mask.isVisible()){
15027             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15028             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15029         }
15030         if(this.isVisible()){
15031             this.constrainXY();
15032         }
15033     },
15034
15035     /**
15036      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15037      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15038      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15039      */
15040     destroy : function(removeEl){
15041         if(this.isVisible()){
15042             this.animateTarget = null;
15043             this.hide();
15044         }
15045         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15046         if(this.tabs){
15047             this.tabs.destroy(removeEl);
15048         }
15049         Roo.destroy(
15050              this.shim,
15051              this.proxy,
15052              this.resizer,
15053              this.close,
15054              this.mask
15055         );
15056         if(this.dd){
15057             this.dd.unreg();
15058         }
15059         if(this.buttons){
15060            for(var i = 0, len = this.buttons.length; i < len; i++){
15061                this.buttons[i].destroy();
15062            }
15063         }
15064         this.el.removeAllListeners();
15065         if(removeEl === true){
15066             this.el.update("");
15067             this.el.remove();
15068         }
15069         Roo.DialogManager.unregister(this);
15070     },
15071
15072     // private
15073     startMove : function(){
15074         if(this.proxyDrag){
15075             this.proxy.show();
15076         }
15077         if(this.constraintoviewport !== false){
15078             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15079         }
15080     },
15081
15082     // private
15083     endMove : function(){
15084         if(!this.proxyDrag){
15085             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15086         }else{
15087             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15088             this.proxy.hide();
15089         }
15090         this.refreshSize();
15091         this.adjustAssets();
15092         this.focus();
15093         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15094     },
15095
15096     /**
15097      * Brings this dialog to the front of any other visible dialogs
15098      * @return {Roo.BasicDialog} this
15099      */
15100     toFront : function(){
15101         Roo.DialogManager.bringToFront(this);
15102         return this;
15103     },
15104
15105     /**
15106      * Sends this dialog to the back (under) of any other visible dialogs
15107      * @return {Roo.BasicDialog} this
15108      */
15109     toBack : function(){
15110         Roo.DialogManager.sendToBack(this);
15111         return this;
15112     },
15113
15114     /**
15115      * Centers this dialog in the viewport
15116      * @return {Roo.BasicDialog} this
15117      */
15118     center : function(){
15119         var xy = this.el.getCenterXY(true);
15120         this.moveTo(xy[0], xy[1]);
15121         return this;
15122     },
15123
15124     /**
15125      * Moves the dialog's top-left corner to the specified point
15126      * @param {Number} x
15127      * @param {Number} y
15128      * @return {Roo.BasicDialog} this
15129      */
15130     moveTo : function(x, y){
15131         this.xy = [x,y];
15132         if(this.isVisible()){
15133             this.el.setXY(this.xy);
15134             this.adjustAssets();
15135         }
15136         return this;
15137     },
15138
15139     /**
15140      * Aligns the dialog to the specified element
15141      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15142      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15143      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15144      * @return {Roo.BasicDialog} this
15145      */
15146     alignTo : function(element, position, offsets){
15147         this.xy = this.el.getAlignToXY(element, position, offsets);
15148         if(this.isVisible()){
15149             this.el.setXY(this.xy);
15150             this.adjustAssets();
15151         }
15152         return this;
15153     },
15154
15155     /**
15156      * Anchors an element to another element and realigns it when the window is resized.
15157      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15158      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15159      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15160      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15161      * is a number, it is used as the buffer delay (defaults to 50ms).
15162      * @return {Roo.BasicDialog} this
15163      */
15164     anchorTo : function(el, alignment, offsets, monitorScroll){
15165         var action = function(){
15166             this.alignTo(el, alignment, offsets);
15167         };
15168         Roo.EventManager.onWindowResize(action, this);
15169         var tm = typeof monitorScroll;
15170         if(tm != 'undefined'){
15171             Roo.EventManager.on(window, 'scroll', action, this,
15172                 {buffer: tm == 'number' ? monitorScroll : 50});
15173         }
15174         action.call(this);
15175         return this;
15176     },
15177
15178     /**
15179      * Returns true if the dialog is visible
15180      * @return {Boolean}
15181      */
15182     isVisible : function(){
15183         return this.el.isVisible();
15184     },
15185
15186     // private
15187     animHide : function(callback){
15188         var b = Roo.get(this.animateTarget).getBox();
15189         this.proxy.show();
15190         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15191         this.el.hide();
15192         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15193                     this.hideEl.createDelegate(this, [callback]));
15194     },
15195
15196     /**
15197      * Hides the dialog.
15198      * @param {Function} callback (optional) Function to call when the dialog is hidden
15199      * @return {Roo.BasicDialog} this
15200      */
15201     hide : function(callback){
15202         if (this.fireEvent("beforehide", this) === false){
15203             return;
15204         }
15205         if(this.shadow){
15206             this.shadow.hide();
15207         }
15208         if(this.shim) {
15209           this.shim.hide();
15210         }
15211         // sometimes animateTarget seems to get set.. causing problems...
15212         // this just double checks..
15213         if(this.animateTarget && Roo.get(this.animateTarget)) {
15214            this.animHide(callback);
15215         }else{
15216             this.el.hide();
15217             this.hideEl(callback);
15218         }
15219         return this;
15220     },
15221
15222     // private
15223     hideEl : function(callback){
15224         this.proxy.hide();
15225         if(this.modal){
15226             this.mask.hide();
15227             Roo.get(document.body).removeClass("x-body-masked");
15228         }
15229         this.fireEvent("hide", this);
15230         if(typeof callback == "function"){
15231             callback();
15232         }
15233     },
15234
15235     // private
15236     hideAction : function(){
15237         this.setLeft("-10000px");
15238         this.setTop("-10000px");
15239         this.setStyle("visibility", "hidden");
15240     },
15241
15242     // private
15243     refreshSize : function(){
15244         this.size = this.el.getSize();
15245         this.xy = this.el.getXY();
15246         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15247     },
15248
15249     // private
15250     // z-index is managed by the DialogManager and may be overwritten at any time
15251     setZIndex : function(index){
15252         if(this.modal){
15253             this.mask.setStyle("z-index", index);
15254         }
15255         if(this.shim){
15256             this.shim.setStyle("z-index", ++index);
15257         }
15258         if(this.shadow){
15259             this.shadow.setZIndex(++index);
15260         }
15261         this.el.setStyle("z-index", ++index);
15262         if(this.proxy){
15263             this.proxy.setStyle("z-index", ++index);
15264         }
15265         if(this.resizer){
15266             this.resizer.proxy.setStyle("z-index", ++index);
15267         }
15268
15269         this.lastZIndex = index;
15270     },
15271
15272     /**
15273      * Returns the element for this dialog
15274      * @return {Roo.Element} The underlying dialog Element
15275      */
15276     getEl : function(){
15277         return this.el;
15278     }
15279 });
15280
15281 /**
15282  * @class Roo.DialogManager
15283  * Provides global access to BasicDialogs that have been created and
15284  * support for z-indexing (layering) multiple open dialogs.
15285  */
15286 Roo.DialogManager = function(){
15287     var list = {};
15288     var accessList = [];
15289     var front = null;
15290
15291     // private
15292     var sortDialogs = function(d1, d2){
15293         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15294     };
15295
15296     // private
15297     var orderDialogs = function(){
15298         accessList.sort(sortDialogs);
15299         var seed = Roo.DialogManager.zseed;
15300         for(var i = 0, len = accessList.length; i < len; i++){
15301             var dlg = accessList[i];
15302             if(dlg){
15303                 dlg.setZIndex(seed + (i*10));
15304             }
15305         }
15306     };
15307
15308     return {
15309         /**
15310          * The starting z-index for BasicDialogs (defaults to 9000)
15311          * @type Number The z-index value
15312          */
15313         zseed : 9000,
15314
15315         // private
15316         register : function(dlg){
15317             list[dlg.id] = dlg;
15318             accessList.push(dlg);
15319         },
15320
15321         // private
15322         unregister : function(dlg){
15323             delete list[dlg.id];
15324             var i=0;
15325             var len=0;
15326             if(!accessList.indexOf){
15327                 for(  i = 0, len = accessList.length; i < len; i++){
15328                     if(accessList[i] == dlg){
15329                         accessList.splice(i, 1);
15330                         return;
15331                     }
15332                 }
15333             }else{
15334                  i = accessList.indexOf(dlg);
15335                 if(i != -1){
15336                     accessList.splice(i, 1);
15337                 }
15338             }
15339         },
15340
15341         /**
15342          * Gets a registered dialog by id
15343          * @param {String/Object} id The id of the dialog or a dialog
15344          * @return {Roo.BasicDialog} this
15345          */
15346         get : function(id){
15347             return typeof id == "object" ? id : list[id];
15348         },
15349
15350         /**
15351          * Brings the specified dialog to the front
15352          * @param {String/Object} dlg The id of the dialog or a dialog
15353          * @return {Roo.BasicDialog} this
15354          */
15355         bringToFront : function(dlg){
15356             dlg = this.get(dlg);
15357             if(dlg != front){
15358                 front = dlg;
15359                 dlg._lastAccess = new Date().getTime();
15360                 orderDialogs();
15361             }
15362             return dlg;
15363         },
15364
15365         /**
15366          * Sends the specified dialog to the back
15367          * @param {String/Object} dlg The id of the dialog or a dialog
15368          * @return {Roo.BasicDialog} this
15369          */
15370         sendToBack : function(dlg){
15371             dlg = this.get(dlg);
15372             dlg._lastAccess = -(new Date().getTime());
15373             orderDialogs();
15374             return dlg;
15375         },
15376
15377         /**
15378          * Hides all dialogs
15379          */
15380         hideAll : function(){
15381             for(var id in list){
15382                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15383                     list[id].hide();
15384                 }
15385             }
15386         }
15387     };
15388 }();
15389
15390 /**
15391  * @class Roo.LayoutDialog
15392  * @extends Roo.BasicDialog
15393  * Dialog which provides adjustments for working with a layout in a Dialog.
15394  * Add your necessary layout config options to the dialog's config.<br>
15395  * Example usage (including a nested layout):
15396  * <pre><code>
15397 if(!dialog){
15398     dialog = new Roo.LayoutDialog("download-dlg", {
15399         modal: true,
15400         width:600,
15401         height:450,
15402         shadow:true,
15403         minWidth:500,
15404         minHeight:350,
15405         autoTabs:true,
15406         proxyDrag:true,
15407         // layout config merges with the dialog config
15408         center:{
15409             tabPosition: "top",
15410             alwaysShowTabs: true
15411         }
15412     });
15413     dialog.addKeyListener(27, dialog.hide, dialog);
15414     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15415     dialog.addButton("Build It!", this.getDownload, this);
15416
15417     // we can even add nested layouts
15418     var innerLayout = new Roo.BorderLayout("dl-inner", {
15419         east: {
15420             initialSize: 200,
15421             autoScroll:true,
15422             split:true
15423         },
15424         center: {
15425             autoScroll:true
15426         }
15427     });
15428     innerLayout.beginUpdate();
15429     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15430     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15431     innerLayout.endUpdate(true);
15432
15433     var layout = dialog.getLayout();
15434     layout.beginUpdate();
15435     layout.add("center", new Roo.ContentPanel("standard-panel",
15436                         {title: "Download the Source", fitToFrame:true}));
15437     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15438                {title: "Build your own roo.js"}));
15439     layout.getRegion("center").showPanel(sp);
15440     layout.endUpdate();
15441 }
15442 </code></pre>
15443     * @constructor
15444     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15445     * @param {Object} config configuration options
15446   */
15447 Roo.LayoutDialog = function(el, cfg){
15448     
15449     var config=  cfg;
15450     if (typeof(cfg) == 'undefined') {
15451         config = Roo.apply({}, el);
15452         // not sure why we use documentElement here.. - it should always be body.
15453         // IE7 borks horribly if we use documentElement.
15454         // webkit also does not like documentElement - it creates a body element...
15455         el = Roo.get( document.body || document.documentElement ).createChild();
15456         //config.autoCreate = true;
15457     }
15458     
15459     
15460     config.autoTabs = false;
15461     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15462     this.body.setStyle({overflow:"hidden", position:"relative"});
15463     this.layout = new Roo.BorderLayout(this.body.dom, config);
15464     this.layout.monitorWindowResize = false;
15465     this.el.addClass("x-dlg-auto-layout");
15466     // fix case when center region overwrites center function
15467     this.center = Roo.BasicDialog.prototype.center;
15468     this.on("show", this.layout.layout, this.layout, true);
15469     if (config.items) {
15470         var xitems = config.items;
15471         delete config.items;
15472         Roo.each(xitems, this.addxtype, this);
15473     }
15474     
15475     
15476 };
15477 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15478     /**
15479      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15480      * @deprecated
15481      */
15482     endUpdate : function(){
15483         this.layout.endUpdate();
15484     },
15485
15486     /**
15487      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15488      *  @deprecated
15489      */
15490     beginUpdate : function(){
15491         this.layout.beginUpdate();
15492     },
15493
15494     /**
15495      * Get the BorderLayout for this dialog
15496      * @return {Roo.BorderLayout}
15497      */
15498     getLayout : function(){
15499         return this.layout;
15500     },
15501
15502     showEl : function(){
15503         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15504         if(Roo.isIE7){
15505             this.layout.layout();
15506         }
15507     },
15508
15509     // private
15510     // Use the syncHeightBeforeShow config option to control this automatically
15511     syncBodyHeight : function(){
15512         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15513         if(this.layout){this.layout.layout();}
15514     },
15515     
15516       /**
15517      * Add an xtype element (actually adds to the layout.)
15518      * @return {Object} xdata xtype object data.
15519      */
15520     
15521     addxtype : function(c) {
15522         return this.layout.addxtype(c);
15523     }
15524 });/*
15525  * Based on:
15526  * Ext JS Library 1.1.1
15527  * Copyright(c) 2006-2007, Ext JS, LLC.
15528  *
15529  * Originally Released Under LGPL - original licence link has changed is not relivant.
15530  *
15531  * Fork - LGPL
15532  * <script type="text/javascript">
15533  */
15534  
15535 /**
15536  * @class Roo.MessageBox
15537  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15538  * Example usage:
15539  *<pre><code>
15540 // Basic alert:
15541 Roo.Msg.alert('Status', 'Changes saved successfully.');
15542
15543 // Prompt for user data:
15544 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15545     if (btn == 'ok'){
15546         // process text value...
15547     }
15548 });
15549
15550 // Show a dialog using config options:
15551 Roo.Msg.show({
15552    title:'Save Changes?',
15553    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15554    buttons: Roo.Msg.YESNOCANCEL,
15555    fn: processResult,
15556    animEl: 'elId'
15557 });
15558 </code></pre>
15559  * @singleton
15560  */
15561 Roo.MessageBox = function(){
15562     var dlg, opt, mask, waitTimer;
15563     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15564     var buttons, activeTextEl, bwidth;
15565
15566     // private
15567     var handleButton = function(button){
15568         dlg.hide();
15569         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15570     };
15571
15572     // private
15573     var handleHide = function(){
15574         if(opt && opt.cls){
15575             dlg.el.removeClass(opt.cls);
15576         }
15577         if(waitTimer){
15578             Roo.TaskMgr.stop(waitTimer);
15579             waitTimer = null;
15580         }
15581     };
15582
15583     // private
15584     var updateButtons = function(b){
15585         var width = 0;
15586         if(!b){
15587             buttons["ok"].hide();
15588             buttons["cancel"].hide();
15589             buttons["yes"].hide();
15590             buttons["no"].hide();
15591             dlg.footer.dom.style.display = 'none';
15592             return width;
15593         }
15594         dlg.footer.dom.style.display = '';
15595         for(var k in buttons){
15596             if(typeof buttons[k] != "function"){
15597                 if(b[k]){
15598                     buttons[k].show();
15599                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15600                     width += buttons[k].el.getWidth()+15;
15601                 }else{
15602                     buttons[k].hide();
15603                 }
15604             }
15605         }
15606         return width;
15607     };
15608
15609     // private
15610     var handleEsc = function(d, k, e){
15611         if(opt && opt.closable !== false){
15612             dlg.hide();
15613         }
15614         if(e){
15615             e.stopEvent();
15616         }
15617     };
15618
15619     return {
15620         /**
15621          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15622          * @return {Roo.BasicDialog} The BasicDialog element
15623          */
15624         getDialog : function(){
15625            if(!dlg){
15626                 dlg = new Roo.BasicDialog("x-msg-box", {
15627                     autoCreate : true,
15628                     shadow: true,
15629                     draggable: true,
15630                     resizable:false,
15631                     constraintoviewport:false,
15632                     fixedcenter:true,
15633                     collapsible : false,
15634                     shim:true,
15635                     modal: true,
15636                     width:400, height:100,
15637                     buttonAlign:"center",
15638                     closeClick : function(){
15639                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15640                             handleButton("no");
15641                         }else{
15642                             handleButton("cancel");
15643                         }
15644                     }
15645                 });
15646                 dlg.on("hide", handleHide);
15647                 mask = dlg.mask;
15648                 dlg.addKeyListener(27, handleEsc);
15649                 buttons = {};
15650                 var bt = this.buttonText;
15651                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15652                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15653                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15654                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15655                 bodyEl = dlg.body.createChild({
15656
15657                     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>'
15658                 });
15659                 msgEl = bodyEl.dom.firstChild;
15660                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15661                 textboxEl.enableDisplayMode();
15662                 textboxEl.addKeyListener([10,13], function(){
15663                     if(dlg.isVisible() && opt && opt.buttons){
15664                         if(opt.buttons.ok){
15665                             handleButton("ok");
15666                         }else if(opt.buttons.yes){
15667                             handleButton("yes");
15668                         }
15669                     }
15670                 });
15671                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15672                 textareaEl.enableDisplayMode();
15673                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15674                 progressEl.enableDisplayMode();
15675                 var pf = progressEl.dom.firstChild;
15676                 if (pf) {
15677                     pp = Roo.get(pf.firstChild);
15678                     pp.setHeight(pf.offsetHeight);
15679                 }
15680                 
15681             }
15682             return dlg;
15683         },
15684
15685         /**
15686          * Updates the message box body text
15687          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15688          * the XHTML-compliant non-breaking space character '&amp;#160;')
15689          * @return {Roo.MessageBox} This message box
15690          */
15691         updateText : function(text){
15692             if(!dlg.isVisible() && !opt.width){
15693                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15694             }
15695             msgEl.innerHTML = text || '&#160;';
15696       
15697             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15698             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15699             var w = Math.max(
15700                     Math.min(opt.width || cw , this.maxWidth), 
15701                     Math.max(opt.minWidth || this.minWidth, bwidth)
15702             );
15703             if(opt.prompt){
15704                 activeTextEl.setWidth(w);
15705             }
15706             if(dlg.isVisible()){
15707                 dlg.fixedcenter = false;
15708             }
15709             // to big, make it scroll. = But as usual stupid IE does not support
15710             // !important..
15711             
15712             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15713                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15714                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15715             } else {
15716                 bodyEl.dom.style.height = '';
15717                 bodyEl.dom.style.overflowY = '';
15718             }
15719             if (cw > w) {
15720                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15721             } else {
15722                 bodyEl.dom.style.overflowX = '';
15723             }
15724             
15725             dlg.setContentSize(w, bodyEl.getHeight());
15726             if(dlg.isVisible()){
15727                 dlg.fixedcenter = true;
15728             }
15729             return this;
15730         },
15731
15732         /**
15733          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15734          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15735          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15736          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15737          * @return {Roo.MessageBox} This message box
15738          */
15739         updateProgress : function(value, text){
15740             if(text){
15741                 this.updateText(text);
15742             }
15743             if (pp) { // weird bug on my firefox - for some reason this is not defined
15744                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15745             }
15746             return this;
15747         },        
15748
15749         /**
15750          * Returns true if the message box is currently displayed
15751          * @return {Boolean} True if the message box is visible, else false
15752          */
15753         isVisible : function(){
15754             return dlg && dlg.isVisible();  
15755         },
15756
15757         /**
15758          * Hides the message box if it is displayed
15759          */
15760         hide : function(){
15761             if(this.isVisible()){
15762                 dlg.hide();
15763             }  
15764         },
15765
15766         /**
15767          * Displays a new message box, or reinitializes an existing message box, based on the config options
15768          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15769          * The following config object properties are supported:
15770          * <pre>
15771 Property    Type             Description
15772 ----------  ---------------  ------------------------------------------------------------------------------------
15773 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15774                                    closes (defaults to undefined)
15775 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15776                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15777 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15778                                    progress and wait dialogs will ignore this property and always hide the
15779                                    close button as they can only be closed programmatically.
15780 cls               String           A custom CSS class to apply to the message box element
15781 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15782                                    displayed (defaults to 75)
15783 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15784                                    function will be btn (the name of the button that was clicked, if applicable,
15785                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15786                                    Progress and wait dialogs will ignore this option since they do not respond to
15787                                    user actions and can only be closed programmatically, so any required function
15788                                    should be called by the same code after it closes the dialog.
15789 icon              String           A CSS class that provides a background image to be used as an icon for
15790                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15791 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15792 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15793 modal             Boolean          False to allow user interaction with the page while the message box is
15794                                    displayed (defaults to true)
15795 msg               String           A string that will replace the existing message box body text (defaults
15796                                    to the XHTML-compliant non-breaking space character '&#160;')
15797 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15798 progress          Boolean          True to display a progress bar (defaults to false)
15799 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15800 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15801 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15802 title             String           The title text
15803 value             String           The string value to set into the active textbox element if displayed
15804 wait              Boolean          True to display a progress bar (defaults to false)
15805 width             Number           The width of the dialog in pixels
15806 </pre>
15807          *
15808          * Example usage:
15809          * <pre><code>
15810 Roo.Msg.show({
15811    title: 'Address',
15812    msg: 'Please enter your address:',
15813    width: 300,
15814    buttons: Roo.MessageBox.OKCANCEL,
15815    multiline: true,
15816    fn: saveAddress,
15817    animEl: 'addAddressBtn'
15818 });
15819 </code></pre>
15820          * @param {Object} config Configuration options
15821          * @return {Roo.MessageBox} This message box
15822          */
15823         show : function(options)
15824         {
15825             
15826             // this causes nightmares if you show one dialog after another
15827             // especially on callbacks..
15828              
15829             if(this.isVisible()){
15830                 
15831                 this.hide();
15832                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15833                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15834                 Roo.log("New Dialog Message:" +  options.msg )
15835                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15836                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15837                 
15838             }
15839             var d = this.getDialog();
15840             opt = options;
15841             d.setTitle(opt.title || "&#160;");
15842             d.close.setDisplayed(opt.closable !== false);
15843             activeTextEl = textboxEl;
15844             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15845             if(opt.prompt){
15846                 if(opt.multiline){
15847                     textboxEl.hide();
15848                     textareaEl.show();
15849                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15850                         opt.multiline : this.defaultTextHeight);
15851                     activeTextEl = textareaEl;
15852                 }else{
15853                     textboxEl.show();
15854                     textareaEl.hide();
15855                 }
15856             }else{
15857                 textboxEl.hide();
15858                 textareaEl.hide();
15859             }
15860             progressEl.setDisplayed(opt.progress === true);
15861             this.updateProgress(0);
15862             activeTextEl.dom.value = opt.value || "";
15863             if(opt.prompt){
15864                 dlg.setDefaultButton(activeTextEl);
15865             }else{
15866                 var bs = opt.buttons;
15867                 var db = null;
15868                 if(bs && bs.ok){
15869                     db = buttons["ok"];
15870                 }else if(bs && bs.yes){
15871                     db = buttons["yes"];
15872                 }
15873                 dlg.setDefaultButton(db);
15874             }
15875             bwidth = updateButtons(opt.buttons);
15876             this.updateText(opt.msg);
15877             if(opt.cls){
15878                 d.el.addClass(opt.cls);
15879             }
15880             d.proxyDrag = opt.proxyDrag === true;
15881             d.modal = opt.modal !== false;
15882             d.mask = opt.modal !== false ? mask : false;
15883             if(!d.isVisible()){
15884                 // force it to the end of the z-index stack so it gets a cursor in FF
15885                 document.body.appendChild(dlg.el.dom);
15886                 d.animateTarget = null;
15887                 d.show(options.animEl);
15888             }
15889             return this;
15890         },
15891
15892         /**
15893          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15894          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15895          * and closing the message box when the process is complete.
15896          * @param {String} title The title bar text
15897          * @param {String} msg The message box body text
15898          * @return {Roo.MessageBox} This message box
15899          */
15900         progress : function(title, msg){
15901             this.show({
15902                 title : title,
15903                 msg : msg,
15904                 buttons: false,
15905                 progress:true,
15906                 closable:false,
15907                 minWidth: this.minProgressWidth,
15908                 modal : true
15909             });
15910             return this;
15911         },
15912
15913         /**
15914          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15915          * If a callback function is passed it will be called after the user clicks the button, and the
15916          * id of the button that was clicked will be passed as the only parameter to the callback
15917          * (could also be the top-right close button).
15918          * @param {String} title The title bar text
15919          * @param {String} msg The message box body text
15920          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15921          * @param {Object} scope (optional) The scope of the callback function
15922          * @return {Roo.MessageBox} This message box
15923          */
15924         alert : function(title, msg, fn, scope){
15925             this.show({
15926                 title : title,
15927                 msg : msg,
15928                 buttons: this.OK,
15929                 fn: fn,
15930                 scope : scope,
15931                 modal : true
15932             });
15933             return this;
15934         },
15935
15936         /**
15937          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15938          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15939          * You are responsible for closing the message box when the process is complete.
15940          * @param {String} msg The message box body text
15941          * @param {String} title (optional) The title bar text
15942          * @return {Roo.MessageBox} This message box
15943          */
15944         wait : function(msg, title){
15945             this.show({
15946                 title : title,
15947                 msg : msg,
15948                 buttons: false,
15949                 closable:false,
15950                 progress:true,
15951                 modal:true,
15952                 width:300,
15953                 wait:true
15954             });
15955             waitTimer = Roo.TaskMgr.start({
15956                 run: function(i){
15957                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15958                 },
15959                 interval: 1000
15960             });
15961             return this;
15962         },
15963
15964         /**
15965          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15966          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15967          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15968          * @param {String} title The title bar text
15969          * @param {String} msg The message box body text
15970          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15971          * @param {Object} scope (optional) The scope of the callback function
15972          * @return {Roo.MessageBox} This message box
15973          */
15974         confirm : function(title, msg, fn, scope){
15975             this.show({
15976                 title : title,
15977                 msg : msg,
15978                 buttons: this.YESNO,
15979                 fn: fn,
15980                 scope : scope,
15981                 modal : true
15982             });
15983             return this;
15984         },
15985
15986         /**
15987          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15988          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15989          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15990          * (could also be the top-right close button) and the text that was entered will be passed as the two
15991          * parameters to the callback.
15992          * @param {String} title The title bar text
15993          * @param {String} msg The message box body text
15994          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15995          * @param {Object} scope (optional) The scope of the callback function
15996          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15997          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15998          * @return {Roo.MessageBox} This message box
15999          */
16000         prompt : function(title, msg, fn, scope, multiline){
16001             this.show({
16002                 title : title,
16003                 msg : msg,
16004                 buttons: this.OKCANCEL,
16005                 fn: fn,
16006                 minWidth:250,
16007                 scope : scope,
16008                 prompt:true,
16009                 multiline: multiline,
16010                 modal : true
16011             });
16012             return this;
16013         },
16014
16015         /**
16016          * Button config that displays a single OK button
16017          * @type Object
16018          */
16019         OK : {ok:true},
16020         /**
16021          * Button config that displays Yes and No buttons
16022          * @type Object
16023          */
16024         YESNO : {yes:true, no:true},
16025         /**
16026          * Button config that displays OK and Cancel buttons
16027          * @type Object
16028          */
16029         OKCANCEL : {ok:true, cancel:true},
16030         /**
16031          * Button config that displays Yes, No and Cancel buttons
16032          * @type Object
16033          */
16034         YESNOCANCEL : {yes:true, no:true, cancel:true},
16035
16036         /**
16037          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16038          * @type Number
16039          */
16040         defaultTextHeight : 75,
16041         /**
16042          * The maximum width in pixels of the message box (defaults to 600)
16043          * @type Number
16044          */
16045         maxWidth : 600,
16046         /**
16047          * The minimum width in pixels of the message box (defaults to 100)
16048          * @type Number
16049          */
16050         minWidth : 100,
16051         /**
16052          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16053          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16054          * @type Number
16055          */
16056         minProgressWidth : 250,
16057         /**
16058          * An object containing the default button text strings that can be overriden for localized language support.
16059          * Supported properties are: ok, cancel, yes and no.
16060          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16061          * @type Object
16062          */
16063         buttonText : {
16064             ok : "OK",
16065             cancel : "Cancel",
16066             yes : "Yes",
16067             no : "No"
16068         }
16069     };
16070 }();
16071
16072 /**
16073  * Shorthand for {@link Roo.MessageBox}
16074  */
16075 Roo.Msg = Roo.MessageBox;/*
16076  * Based on:
16077  * Ext JS Library 1.1.1
16078  * Copyright(c) 2006-2007, Ext JS, LLC.
16079  *
16080  * Originally Released Under LGPL - original licence link has changed is not relivant.
16081  *
16082  * Fork - LGPL
16083  * <script type="text/javascript">
16084  */
16085 /**
16086  * @class Roo.QuickTips
16087  * Provides attractive and customizable tooltips for any element.
16088  * @singleton
16089  */
16090 Roo.QuickTips = function(){
16091     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16092     var ce, bd, xy, dd;
16093     var visible = false, disabled = true, inited = false;
16094     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16095     
16096     var onOver = function(e){
16097         if(disabled){
16098             return;
16099         }
16100         var t = e.getTarget();
16101         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16102             return;
16103         }
16104         if(ce && t == ce.el){
16105             clearTimeout(hideProc);
16106             return;
16107         }
16108         if(t && tagEls[t.id]){
16109             tagEls[t.id].el = t;
16110             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16111             return;
16112         }
16113         var ttp, et = Roo.fly(t);
16114         var ns = cfg.namespace;
16115         if(tm.interceptTitles && t.title){
16116             ttp = t.title;
16117             t.qtip = ttp;
16118             t.removeAttribute("title");
16119             e.preventDefault();
16120         }else{
16121             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16122         }
16123         if(ttp){
16124             showProc = show.defer(tm.showDelay, tm, [{
16125                 el: t, 
16126                 text: ttp, 
16127                 width: et.getAttributeNS(ns, cfg.width),
16128                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16129                 title: et.getAttributeNS(ns, cfg.title),
16130                     cls: et.getAttributeNS(ns, cfg.cls)
16131             }]);
16132         }
16133     };
16134     
16135     var onOut = function(e){
16136         clearTimeout(showProc);
16137         var t = e.getTarget();
16138         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16139             hideProc = setTimeout(hide, tm.hideDelay);
16140         }
16141     };
16142     
16143     var onMove = function(e){
16144         if(disabled){
16145             return;
16146         }
16147         xy = e.getXY();
16148         xy[1] += 18;
16149         if(tm.trackMouse && ce){
16150             el.setXY(xy);
16151         }
16152     };
16153     
16154     var onDown = function(e){
16155         clearTimeout(showProc);
16156         clearTimeout(hideProc);
16157         if(!e.within(el)){
16158             if(tm.hideOnClick){
16159                 hide();
16160                 tm.disable();
16161                 tm.enable.defer(100, tm);
16162             }
16163         }
16164     };
16165     
16166     var getPad = function(){
16167         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16168     };
16169
16170     var show = function(o){
16171         if(disabled){
16172             return;
16173         }
16174         clearTimeout(dismissProc);
16175         ce = o;
16176         if(removeCls){ // in case manually hidden
16177             el.removeClass(removeCls);
16178             removeCls = null;
16179         }
16180         if(ce.cls){
16181             el.addClass(ce.cls);
16182             removeCls = ce.cls;
16183         }
16184         if(ce.title){
16185             tipTitle.update(ce.title);
16186             tipTitle.show();
16187         }else{
16188             tipTitle.update('');
16189             tipTitle.hide();
16190         }
16191         el.dom.style.width  = tm.maxWidth+'px';
16192         //tipBody.dom.style.width = '';
16193         tipBodyText.update(o.text);
16194         var p = getPad(), w = ce.width;
16195         if(!w){
16196             var td = tipBodyText.dom;
16197             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16198             if(aw > tm.maxWidth){
16199                 w = tm.maxWidth;
16200             }else if(aw < tm.minWidth){
16201                 w = tm.minWidth;
16202             }else{
16203                 w = aw;
16204             }
16205         }
16206         //tipBody.setWidth(w);
16207         el.setWidth(parseInt(w, 10) + p);
16208         if(ce.autoHide === false){
16209             close.setDisplayed(true);
16210             if(dd){
16211                 dd.unlock();
16212             }
16213         }else{
16214             close.setDisplayed(false);
16215             if(dd){
16216                 dd.lock();
16217             }
16218         }
16219         if(xy){
16220             el.avoidY = xy[1]-18;
16221             el.setXY(xy);
16222         }
16223         if(tm.animate){
16224             el.setOpacity(.1);
16225             el.setStyle("visibility", "visible");
16226             el.fadeIn({callback: afterShow});
16227         }else{
16228             afterShow();
16229         }
16230     };
16231     
16232     var afterShow = function(){
16233         if(ce){
16234             el.show();
16235             esc.enable();
16236             if(tm.autoDismiss && ce.autoHide !== false){
16237                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16238             }
16239         }
16240     };
16241     
16242     var hide = function(noanim){
16243         clearTimeout(dismissProc);
16244         clearTimeout(hideProc);
16245         ce = null;
16246         if(el.isVisible()){
16247             esc.disable();
16248             if(noanim !== true && tm.animate){
16249                 el.fadeOut({callback: afterHide});
16250             }else{
16251                 afterHide();
16252             } 
16253         }
16254     };
16255     
16256     var afterHide = function(){
16257         el.hide();
16258         if(removeCls){
16259             el.removeClass(removeCls);
16260             removeCls = null;
16261         }
16262     };
16263     
16264     return {
16265         /**
16266         * @cfg {Number} minWidth
16267         * The minimum width of the quick tip (defaults to 40)
16268         */
16269        minWidth : 40,
16270         /**
16271         * @cfg {Number} maxWidth
16272         * The maximum width of the quick tip (defaults to 300)
16273         */
16274        maxWidth : 300,
16275         /**
16276         * @cfg {Boolean} interceptTitles
16277         * True to automatically use the element's DOM title value if available (defaults to false)
16278         */
16279        interceptTitles : false,
16280         /**
16281         * @cfg {Boolean} trackMouse
16282         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16283         */
16284        trackMouse : false,
16285         /**
16286         * @cfg {Boolean} hideOnClick
16287         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16288         */
16289        hideOnClick : true,
16290         /**
16291         * @cfg {Number} showDelay
16292         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16293         */
16294        showDelay : 500,
16295         /**
16296         * @cfg {Number} hideDelay
16297         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16298         */
16299        hideDelay : 200,
16300         /**
16301         * @cfg {Boolean} autoHide
16302         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16303         * Used in conjunction with hideDelay.
16304         */
16305        autoHide : true,
16306         /**
16307         * @cfg {Boolean}
16308         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16309         * (defaults to true).  Used in conjunction with autoDismissDelay.
16310         */
16311        autoDismiss : true,
16312         /**
16313         * @cfg {Number}
16314         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16315         */
16316        autoDismissDelay : 5000,
16317        /**
16318         * @cfg {Boolean} animate
16319         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16320         */
16321        animate : false,
16322
16323        /**
16324         * @cfg {String} title
16325         * Title text to display (defaults to '').  This can be any valid HTML markup.
16326         */
16327         title: '',
16328        /**
16329         * @cfg {String} text
16330         * Body text to display (defaults to '').  This can be any valid HTML markup.
16331         */
16332         text : '',
16333        /**
16334         * @cfg {String} cls
16335         * A CSS class to apply to the base quick tip element (defaults to '').
16336         */
16337         cls : '',
16338        /**
16339         * @cfg {Number} width
16340         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16341         * minWidth or maxWidth.
16342         */
16343         width : null,
16344
16345     /**
16346      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16347      * or display QuickTips in a page.
16348      */
16349        init : function(){
16350           tm = Roo.QuickTips;
16351           cfg = tm.tagConfig;
16352           if(!inited){
16353               if(!Roo.isReady){ // allow calling of init() before onReady
16354                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16355                   return;
16356               }
16357               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16358               el.fxDefaults = {stopFx: true};
16359               // maximum custom styling
16360               //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>');
16361               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>');              
16362               tipTitle = el.child('h3');
16363               tipTitle.enableDisplayMode("block");
16364               tipBody = el.child('div.x-tip-bd');
16365               tipBodyText = el.child('div.x-tip-bd-inner');
16366               //bdLeft = el.child('div.x-tip-bd-left');
16367               //bdRight = el.child('div.x-tip-bd-right');
16368               close = el.child('div.x-tip-close');
16369               close.enableDisplayMode("block");
16370               close.on("click", hide);
16371               var d = Roo.get(document);
16372               d.on("mousedown", onDown);
16373               d.on("mouseover", onOver);
16374               d.on("mouseout", onOut);
16375               d.on("mousemove", onMove);
16376               esc = d.addKeyListener(27, hide);
16377               esc.disable();
16378               if(Roo.dd.DD){
16379                   dd = el.initDD("default", null, {
16380                       onDrag : function(){
16381                           el.sync();  
16382                       }
16383                   });
16384                   dd.setHandleElId(tipTitle.id);
16385                   dd.lock();
16386               }
16387               inited = true;
16388           }
16389           this.enable(); 
16390        },
16391
16392     /**
16393      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16394      * are supported:
16395      * <pre>
16396 Property    Type                   Description
16397 ----------  ---------------------  ------------------------------------------------------------------------
16398 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16399      * </ul>
16400      * @param {Object} config The config object
16401      */
16402        register : function(config){
16403            var cs = config instanceof Array ? config : arguments;
16404            for(var i = 0, len = cs.length; i < len; i++) {
16405                var c = cs[i];
16406                var target = c.target;
16407                if(target){
16408                    if(target instanceof Array){
16409                        for(var j = 0, jlen = target.length; j < jlen; j++){
16410                            tagEls[target[j]] = c;
16411                        }
16412                    }else{
16413                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16414                    }
16415                }
16416            }
16417        },
16418
16419     /**
16420      * Removes this quick tip from its element and destroys it.
16421      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16422      */
16423        unregister : function(el){
16424            delete tagEls[Roo.id(el)];
16425        },
16426
16427     /**
16428      * Enable this quick tip.
16429      */
16430        enable : function(){
16431            if(inited && disabled){
16432                locks.pop();
16433                if(locks.length < 1){
16434                    disabled = false;
16435                }
16436            }
16437        },
16438
16439     /**
16440      * Disable this quick tip.
16441      */
16442        disable : function(){
16443           disabled = true;
16444           clearTimeout(showProc);
16445           clearTimeout(hideProc);
16446           clearTimeout(dismissProc);
16447           if(ce){
16448               hide(true);
16449           }
16450           locks.push(1);
16451        },
16452
16453     /**
16454      * Returns true if the quick tip is enabled, else false.
16455      */
16456        isEnabled : function(){
16457             return !disabled;
16458        },
16459
16460         // private
16461        tagConfig : {
16462            namespace : "ext",
16463            attribute : "qtip",
16464            width : "width",
16465            target : "target",
16466            title : "qtitle",
16467            hide : "hide",
16468            cls : "qclass"
16469        }
16470    };
16471 }();
16472
16473 // backwards compat
16474 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16475  * Based on:
16476  * Ext JS Library 1.1.1
16477  * Copyright(c) 2006-2007, Ext JS, LLC.
16478  *
16479  * Originally Released Under LGPL - original licence link has changed is not relivant.
16480  *
16481  * Fork - LGPL
16482  * <script type="text/javascript">
16483  */
16484  
16485
16486 /**
16487  * @class Roo.tree.TreePanel
16488  * @extends Roo.data.Tree
16489
16490  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16491  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16492  * @cfg {Boolean} enableDD true to enable drag and drop
16493  * @cfg {Boolean} enableDrag true to enable just drag
16494  * @cfg {Boolean} enableDrop true to enable just drop
16495  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16496  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16497  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16498  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16499  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16500  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16501  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16502  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16503  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16504  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16505  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16506  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16507  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16508  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16509  * @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>
16510  * @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>
16511  * 
16512  * @constructor
16513  * @param {String/HTMLElement/Element} el The container element
16514  * @param {Object} config
16515  */
16516 Roo.tree.TreePanel = function(el, config){
16517     var root = false;
16518     var loader = false;
16519     if (config.root) {
16520         root = config.root;
16521         delete config.root;
16522     }
16523     if (config.loader) {
16524         loader = config.loader;
16525         delete config.loader;
16526     }
16527     
16528     Roo.apply(this, config);
16529     Roo.tree.TreePanel.superclass.constructor.call(this);
16530     this.el = Roo.get(el);
16531     this.el.addClass('x-tree');
16532     //console.log(root);
16533     if (root) {
16534         this.setRootNode( Roo.factory(root, Roo.tree));
16535     }
16536     if (loader) {
16537         this.loader = Roo.factory(loader, Roo.tree);
16538     }
16539    /**
16540     * Read-only. The id of the container element becomes this TreePanel's id.
16541     */
16542     this.id = this.el.id;
16543     this.addEvents({
16544         /**
16545         * @event beforeload
16546         * Fires before a node is loaded, return false to cancel
16547         * @param {Node} node The node being loaded
16548         */
16549         "beforeload" : true,
16550         /**
16551         * @event load
16552         * Fires when a node is loaded
16553         * @param {Node} node The node that was loaded
16554         */
16555         "load" : true,
16556         /**
16557         * @event textchange
16558         * Fires when the text for a node is changed
16559         * @param {Node} node The node
16560         * @param {String} text The new text
16561         * @param {String} oldText The old text
16562         */
16563         "textchange" : true,
16564         /**
16565         * @event beforeexpand
16566         * Fires before a node is expanded, return false to cancel.
16567         * @param {Node} node The node
16568         * @param {Boolean} deep
16569         * @param {Boolean} anim
16570         */
16571         "beforeexpand" : true,
16572         /**
16573         * @event beforecollapse
16574         * Fires before a node is collapsed, return false to cancel.
16575         * @param {Node} node The node
16576         * @param {Boolean} deep
16577         * @param {Boolean} anim
16578         */
16579         "beforecollapse" : true,
16580         /**
16581         * @event expand
16582         * Fires when a node is expanded
16583         * @param {Node} node The node
16584         */
16585         "expand" : true,
16586         /**
16587         * @event disabledchange
16588         * Fires when the disabled status of a node changes
16589         * @param {Node} node The node
16590         * @param {Boolean} disabled
16591         */
16592         "disabledchange" : true,
16593         /**
16594         * @event collapse
16595         * Fires when a node is collapsed
16596         * @param {Node} node The node
16597         */
16598         "collapse" : true,
16599         /**
16600         * @event beforeclick
16601         * Fires before click processing on a node. Return false to cancel the default action.
16602         * @param {Node} node The node
16603         * @param {Roo.EventObject} e The event object
16604         */
16605         "beforeclick":true,
16606         /**
16607         * @event checkchange
16608         * Fires when a node with a checkbox's checked property changes
16609         * @param {Node} this This node
16610         * @param {Boolean} checked
16611         */
16612         "checkchange":true,
16613         /**
16614         * @event click
16615         * Fires when a node is clicked
16616         * @param {Node} node The node
16617         * @param {Roo.EventObject} e The event object
16618         */
16619         "click":true,
16620         /**
16621         * @event dblclick
16622         * Fires when a node is double clicked
16623         * @param {Node} node The node
16624         * @param {Roo.EventObject} e The event object
16625         */
16626         "dblclick":true,
16627         /**
16628         * @event contextmenu
16629         * Fires when a node is right clicked
16630         * @param {Node} node The node
16631         * @param {Roo.EventObject} e The event object
16632         */
16633         "contextmenu":true,
16634         /**
16635         * @event beforechildrenrendered
16636         * Fires right before the child nodes for a node are rendered
16637         * @param {Node} node The node
16638         */
16639         "beforechildrenrendered":true,
16640         /**
16641         * @event startdrag
16642         * Fires when a node starts being dragged
16643         * @param {Roo.tree.TreePanel} this
16644         * @param {Roo.tree.TreeNode} node
16645         * @param {event} e The raw browser event
16646         */ 
16647        "startdrag" : true,
16648        /**
16649         * @event enddrag
16650         * Fires when a drag operation is complete
16651         * @param {Roo.tree.TreePanel} this
16652         * @param {Roo.tree.TreeNode} node
16653         * @param {event} e The raw browser event
16654         */
16655        "enddrag" : true,
16656        /**
16657         * @event dragdrop
16658         * Fires when a dragged node is dropped on a valid DD target
16659         * @param {Roo.tree.TreePanel} this
16660         * @param {Roo.tree.TreeNode} node
16661         * @param {DD} dd The dd it was dropped on
16662         * @param {event} e The raw browser event
16663         */
16664        "dragdrop" : true,
16665        /**
16666         * @event beforenodedrop
16667         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16668         * passed to handlers has the following properties:<br />
16669         * <ul style="padding:5px;padding-left:16px;">
16670         * <li>tree - The TreePanel</li>
16671         * <li>target - The node being targeted for the drop</li>
16672         * <li>data - The drag data from the drag source</li>
16673         * <li>point - The point of the drop - append, above or below</li>
16674         * <li>source - The drag source</li>
16675         * <li>rawEvent - Raw mouse event</li>
16676         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16677         * to be inserted by setting them on this object.</li>
16678         * <li>cancel - Set this to true to cancel the drop.</li>
16679         * </ul>
16680         * @param {Object} dropEvent
16681         */
16682        "beforenodedrop" : true,
16683        /**
16684         * @event nodedrop
16685         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16686         * passed to handlers has the following properties:<br />
16687         * <ul style="padding:5px;padding-left:16px;">
16688         * <li>tree - The TreePanel</li>
16689         * <li>target - The node being targeted for the drop</li>
16690         * <li>data - The drag data from the drag source</li>
16691         * <li>point - The point of the drop - append, above or below</li>
16692         * <li>source - The drag source</li>
16693         * <li>rawEvent - Raw mouse event</li>
16694         * <li>dropNode - Dropped node(s).</li>
16695         * </ul>
16696         * @param {Object} dropEvent
16697         */
16698        "nodedrop" : true,
16699         /**
16700         * @event nodedragover
16701         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16702         * passed to handlers has the following properties:<br />
16703         * <ul style="padding:5px;padding-left:16px;">
16704         * <li>tree - The TreePanel</li>
16705         * <li>target - The node being targeted for the drop</li>
16706         * <li>data - The drag data from the drag source</li>
16707         * <li>point - The point of the drop - append, above or below</li>
16708         * <li>source - The drag source</li>
16709         * <li>rawEvent - Raw mouse event</li>
16710         * <li>dropNode - Drop node(s) provided by the source.</li>
16711         * <li>cancel - Set this to true to signal drop not allowed.</li>
16712         * </ul>
16713         * @param {Object} dragOverEvent
16714         */
16715        "nodedragover" : true
16716         
16717     });
16718     if(this.singleExpand){
16719        this.on("beforeexpand", this.restrictExpand, this);
16720     }
16721     if (this.editor) {
16722         this.editor.tree = this;
16723         this.editor = Roo.factory(this.editor, Roo.tree);
16724     }
16725     
16726     if (this.selModel) {
16727         this.selModel = Roo.factory(this.selModel, Roo.tree);
16728     }
16729    
16730 };
16731 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16732     rootVisible : true,
16733     animate: Roo.enableFx,
16734     lines : true,
16735     enableDD : false,
16736     hlDrop : Roo.enableFx,
16737   
16738     renderer: false,
16739     
16740     rendererTip: false,
16741     // private
16742     restrictExpand : function(node){
16743         var p = node.parentNode;
16744         if(p){
16745             if(p.expandedChild && p.expandedChild.parentNode == p){
16746                 p.expandedChild.collapse();
16747             }
16748             p.expandedChild = node;
16749         }
16750     },
16751
16752     // private override
16753     setRootNode : function(node){
16754         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16755         if(!this.rootVisible){
16756             node.ui = new Roo.tree.RootTreeNodeUI(node);
16757         }
16758         return node;
16759     },
16760
16761     /**
16762      * Returns the container element for this TreePanel
16763      */
16764     getEl : function(){
16765         return this.el;
16766     },
16767
16768     /**
16769      * Returns the default TreeLoader for this TreePanel
16770      */
16771     getLoader : function(){
16772         return this.loader;
16773     },
16774
16775     /**
16776      * Expand all nodes
16777      */
16778     expandAll : function(){
16779         this.root.expand(true);
16780     },
16781
16782     /**
16783      * Collapse all nodes
16784      */
16785     collapseAll : function(){
16786         this.root.collapse(true);
16787     },
16788
16789     /**
16790      * Returns the selection model used by this TreePanel
16791      */
16792     getSelectionModel : function(){
16793         if(!this.selModel){
16794             this.selModel = new Roo.tree.DefaultSelectionModel();
16795         }
16796         return this.selModel;
16797     },
16798
16799     /**
16800      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16801      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16802      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16803      * @return {Array}
16804      */
16805     getChecked : function(a, startNode){
16806         startNode = startNode || this.root;
16807         var r = [];
16808         var f = function(){
16809             if(this.attributes.checked){
16810                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16811             }
16812         }
16813         startNode.cascade(f);
16814         return r;
16815     },
16816
16817     /**
16818      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16819      * @param {String} path
16820      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16821      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16822      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16823      */
16824     expandPath : function(path, attr, callback){
16825         attr = attr || "id";
16826         var keys = path.split(this.pathSeparator);
16827         var curNode = this.root;
16828         if(curNode.attributes[attr] != keys[1]){ // invalid root
16829             if(callback){
16830                 callback(false, null);
16831             }
16832             return;
16833         }
16834         var index = 1;
16835         var f = function(){
16836             if(++index == keys.length){
16837                 if(callback){
16838                     callback(true, curNode);
16839                 }
16840                 return;
16841             }
16842             var c = curNode.findChild(attr, keys[index]);
16843             if(!c){
16844                 if(callback){
16845                     callback(false, curNode);
16846                 }
16847                 return;
16848             }
16849             curNode = c;
16850             c.expand(false, false, f);
16851         };
16852         curNode.expand(false, false, f);
16853     },
16854
16855     /**
16856      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16857      * @param {String} path
16858      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16859      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16860      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16861      */
16862     selectPath : function(path, attr, callback){
16863         attr = attr || "id";
16864         var keys = path.split(this.pathSeparator);
16865         var v = keys.pop();
16866         if(keys.length > 0){
16867             var f = function(success, node){
16868                 if(success && node){
16869                     var n = node.findChild(attr, v);
16870                     if(n){
16871                         n.select();
16872                         if(callback){
16873                             callback(true, n);
16874                         }
16875                     }else if(callback){
16876                         callback(false, n);
16877                     }
16878                 }else{
16879                     if(callback){
16880                         callback(false, n);
16881                     }
16882                 }
16883             };
16884             this.expandPath(keys.join(this.pathSeparator), attr, f);
16885         }else{
16886             this.root.select();
16887             if(callback){
16888                 callback(true, this.root);
16889             }
16890         }
16891     },
16892
16893     getTreeEl : function(){
16894         return this.el;
16895     },
16896
16897     /**
16898      * Trigger rendering of this TreePanel
16899      */
16900     render : function(){
16901         if (this.innerCt) {
16902             return this; // stop it rendering more than once!!
16903         }
16904         
16905         this.innerCt = this.el.createChild({tag:"ul",
16906                cls:"x-tree-root-ct " +
16907                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16908
16909         if(this.containerScroll){
16910             Roo.dd.ScrollManager.register(this.el);
16911         }
16912         if((this.enableDD || this.enableDrop) && !this.dropZone){
16913            /**
16914             * The dropZone used by this tree if drop is enabled
16915             * @type Roo.tree.TreeDropZone
16916             */
16917              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16918                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16919            });
16920         }
16921         if((this.enableDD || this.enableDrag) && !this.dragZone){
16922            /**
16923             * The dragZone used by this tree if drag is enabled
16924             * @type Roo.tree.TreeDragZone
16925             */
16926             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16927                ddGroup: this.ddGroup || "TreeDD",
16928                scroll: this.ddScroll
16929            });
16930         }
16931         this.getSelectionModel().init(this);
16932         if (!this.root) {
16933             Roo.log("ROOT not set in tree");
16934             return this;
16935         }
16936         this.root.render();
16937         if(!this.rootVisible){
16938             this.root.renderChildren();
16939         }
16940         return this;
16941     }
16942 });/*
16943  * Based on:
16944  * Ext JS Library 1.1.1
16945  * Copyright(c) 2006-2007, Ext JS, LLC.
16946  *
16947  * Originally Released Under LGPL - original licence link has changed is not relivant.
16948  *
16949  * Fork - LGPL
16950  * <script type="text/javascript">
16951  */
16952  
16953
16954 /**
16955  * @class Roo.tree.DefaultSelectionModel
16956  * @extends Roo.util.Observable
16957  * The default single selection for a TreePanel.
16958  * @param {Object} cfg Configuration
16959  */
16960 Roo.tree.DefaultSelectionModel = function(cfg){
16961    this.selNode = null;
16962    
16963    
16964    
16965    this.addEvents({
16966        /**
16967         * @event selectionchange
16968         * Fires when the selected node changes
16969         * @param {DefaultSelectionModel} this
16970         * @param {TreeNode} node the new selection
16971         */
16972        "selectionchange" : true,
16973
16974        /**
16975         * @event beforeselect
16976         * Fires before the selected node changes, return false to cancel the change
16977         * @param {DefaultSelectionModel} this
16978         * @param {TreeNode} node the new selection
16979         * @param {TreeNode} node the old selection
16980         */
16981        "beforeselect" : true
16982    });
16983    
16984     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16985 };
16986
16987 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16988     init : function(tree){
16989         this.tree = tree;
16990         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16991         tree.on("click", this.onNodeClick, this);
16992     },
16993     
16994     onNodeClick : function(node, e){
16995         if (e.ctrlKey && this.selNode == node)  {
16996             this.unselect(node);
16997             return;
16998         }
16999         this.select(node);
17000     },
17001     
17002     /**
17003      * Select a node.
17004      * @param {TreeNode} node The node to select
17005      * @return {TreeNode} The selected node
17006      */
17007     select : function(node){
17008         var last = this.selNode;
17009         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17010             if(last){
17011                 last.ui.onSelectedChange(false);
17012             }
17013             this.selNode = node;
17014             node.ui.onSelectedChange(true);
17015             this.fireEvent("selectionchange", this, node, last);
17016         }
17017         return node;
17018     },
17019     
17020     /**
17021      * Deselect a node.
17022      * @param {TreeNode} node The node to unselect
17023      */
17024     unselect : function(node){
17025         if(this.selNode == node){
17026             this.clearSelections();
17027         }    
17028     },
17029     
17030     /**
17031      * Clear all selections
17032      */
17033     clearSelections : function(){
17034         var n = this.selNode;
17035         if(n){
17036             n.ui.onSelectedChange(false);
17037             this.selNode = null;
17038             this.fireEvent("selectionchange", this, null);
17039         }
17040         return n;
17041     },
17042     
17043     /**
17044      * Get the selected node
17045      * @return {TreeNode} The selected node
17046      */
17047     getSelectedNode : function(){
17048         return this.selNode;    
17049     },
17050     
17051     /**
17052      * Returns true if the node is selected
17053      * @param {TreeNode} node The node to check
17054      * @return {Boolean}
17055      */
17056     isSelected : function(node){
17057         return this.selNode == node;  
17058     },
17059
17060     /**
17061      * Selects the node above the selected node in the tree, intelligently walking the nodes
17062      * @return TreeNode The new selection
17063      */
17064     selectPrevious : function(){
17065         var s = this.selNode || this.lastSelNode;
17066         if(!s){
17067             return null;
17068         }
17069         var ps = s.previousSibling;
17070         if(ps){
17071             if(!ps.isExpanded() || ps.childNodes.length < 1){
17072                 return this.select(ps);
17073             } else{
17074                 var lc = ps.lastChild;
17075                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17076                     lc = lc.lastChild;
17077                 }
17078                 return this.select(lc);
17079             }
17080         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17081             return this.select(s.parentNode);
17082         }
17083         return null;
17084     },
17085
17086     /**
17087      * Selects the node above the selected node in the tree, intelligently walking the nodes
17088      * @return TreeNode The new selection
17089      */
17090     selectNext : function(){
17091         var s = this.selNode || this.lastSelNode;
17092         if(!s){
17093             return null;
17094         }
17095         if(s.firstChild && s.isExpanded()){
17096              return this.select(s.firstChild);
17097          }else if(s.nextSibling){
17098              return this.select(s.nextSibling);
17099          }else if(s.parentNode){
17100             var newS = null;
17101             s.parentNode.bubble(function(){
17102                 if(this.nextSibling){
17103                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17104                     return false;
17105                 }
17106             });
17107             return newS;
17108          }
17109         return null;
17110     },
17111
17112     onKeyDown : function(e){
17113         var s = this.selNode || this.lastSelNode;
17114         // undesirable, but required
17115         var sm = this;
17116         if(!s){
17117             return;
17118         }
17119         var k = e.getKey();
17120         switch(k){
17121              case e.DOWN:
17122                  e.stopEvent();
17123                  this.selectNext();
17124              break;
17125              case e.UP:
17126                  e.stopEvent();
17127                  this.selectPrevious();
17128              break;
17129              case e.RIGHT:
17130                  e.preventDefault();
17131                  if(s.hasChildNodes()){
17132                      if(!s.isExpanded()){
17133                          s.expand();
17134                      }else if(s.firstChild){
17135                          this.select(s.firstChild, e);
17136                      }
17137                  }
17138              break;
17139              case e.LEFT:
17140                  e.preventDefault();
17141                  if(s.hasChildNodes() && s.isExpanded()){
17142                      s.collapse();
17143                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17144                      this.select(s.parentNode, e);
17145                  }
17146              break;
17147         };
17148     }
17149 });
17150
17151 /**
17152  * @class Roo.tree.MultiSelectionModel
17153  * @extends Roo.util.Observable
17154  * Multi selection for a TreePanel.
17155  * @param {Object} cfg Configuration
17156  */
17157 Roo.tree.MultiSelectionModel = function(){
17158    this.selNodes = [];
17159    this.selMap = {};
17160    this.addEvents({
17161        /**
17162         * @event selectionchange
17163         * Fires when the selected nodes change
17164         * @param {MultiSelectionModel} this
17165         * @param {Array} nodes Array of the selected nodes
17166         */
17167        "selectionchange" : true
17168    });
17169    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17170    
17171 };
17172
17173 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17174     init : function(tree){
17175         this.tree = tree;
17176         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17177         tree.on("click", this.onNodeClick, this);
17178     },
17179     
17180     onNodeClick : function(node, e){
17181         this.select(node, e, e.ctrlKey);
17182     },
17183     
17184     /**
17185      * Select a node.
17186      * @param {TreeNode} node The node to select
17187      * @param {EventObject} e (optional) An event associated with the selection
17188      * @param {Boolean} keepExisting True to retain existing selections
17189      * @return {TreeNode} The selected node
17190      */
17191     select : function(node, e, keepExisting){
17192         if(keepExisting !== true){
17193             this.clearSelections(true);
17194         }
17195         if(this.isSelected(node)){
17196             this.lastSelNode = node;
17197             return node;
17198         }
17199         this.selNodes.push(node);
17200         this.selMap[node.id] = node;
17201         this.lastSelNode = node;
17202         node.ui.onSelectedChange(true);
17203         this.fireEvent("selectionchange", this, this.selNodes);
17204         return node;
17205     },
17206     
17207     /**
17208      * Deselect a node.
17209      * @param {TreeNode} node The node to unselect
17210      */
17211     unselect : function(node){
17212         if(this.selMap[node.id]){
17213             node.ui.onSelectedChange(false);
17214             var sn = this.selNodes;
17215             var index = -1;
17216             if(sn.indexOf){
17217                 index = sn.indexOf(node);
17218             }else{
17219                 for(var i = 0, len = sn.length; i < len; i++){
17220                     if(sn[i] == node){
17221                         index = i;
17222                         break;
17223                     }
17224                 }
17225             }
17226             if(index != -1){
17227                 this.selNodes.splice(index, 1);
17228             }
17229             delete this.selMap[node.id];
17230             this.fireEvent("selectionchange", this, this.selNodes);
17231         }
17232     },
17233     
17234     /**
17235      * Clear all selections
17236      */
17237     clearSelections : function(suppressEvent){
17238         var sn = this.selNodes;
17239         if(sn.length > 0){
17240             for(var i = 0, len = sn.length; i < len; i++){
17241                 sn[i].ui.onSelectedChange(false);
17242             }
17243             this.selNodes = [];
17244             this.selMap = {};
17245             if(suppressEvent !== true){
17246                 this.fireEvent("selectionchange", this, this.selNodes);
17247             }
17248         }
17249     },
17250     
17251     /**
17252      * Returns true if the node is selected
17253      * @param {TreeNode} node The node to check
17254      * @return {Boolean}
17255      */
17256     isSelected : function(node){
17257         return this.selMap[node.id] ? true : false;  
17258     },
17259     
17260     /**
17261      * Returns an array of the selected nodes
17262      * @return {Array}
17263      */
17264     getSelectedNodes : function(){
17265         return this.selNodes;    
17266     },
17267
17268     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17269
17270     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17271
17272     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17273 });/*
17274  * Based on:
17275  * Ext JS Library 1.1.1
17276  * Copyright(c) 2006-2007, Ext JS, LLC.
17277  *
17278  * Originally Released Under LGPL - original licence link has changed is not relivant.
17279  *
17280  * Fork - LGPL
17281  * <script type="text/javascript">
17282  */
17283  
17284 /**
17285  * @class Roo.tree.TreeNode
17286  * @extends Roo.data.Node
17287  * @cfg {String} text The text for this node
17288  * @cfg {Boolean} expanded true to start the node expanded
17289  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17290  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17291  * @cfg {Boolean} disabled true to start the node disabled
17292  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17293  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17294  * @cfg {String} cls A css class to be added to the node
17295  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17296  * @cfg {String} href URL of the link used for the node (defaults to #)
17297  * @cfg {String} hrefTarget target frame for the link
17298  * @cfg {String} qtip An Ext QuickTip for the node
17299  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17300  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17301  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17302  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17303  * (defaults to undefined with no checkbox rendered)
17304  * @constructor
17305  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17306  */
17307 Roo.tree.TreeNode = function(attributes){
17308     attributes = attributes || {};
17309     if(typeof attributes == "string"){
17310         attributes = {text: attributes};
17311     }
17312     this.childrenRendered = false;
17313     this.rendered = false;
17314     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17315     this.expanded = attributes.expanded === true;
17316     this.isTarget = attributes.isTarget !== false;
17317     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17318     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17319
17320     /**
17321      * Read-only. The text for this node. To change it use setText().
17322      * @type String
17323      */
17324     this.text = attributes.text;
17325     /**
17326      * True if this node is disabled.
17327      * @type Boolean
17328      */
17329     this.disabled = attributes.disabled === true;
17330
17331     this.addEvents({
17332         /**
17333         * @event textchange
17334         * Fires when the text for this node is changed
17335         * @param {Node} this This node
17336         * @param {String} text The new text
17337         * @param {String} oldText The old text
17338         */
17339         "textchange" : true,
17340         /**
17341         * @event beforeexpand
17342         * Fires before this node is expanded, return false to cancel.
17343         * @param {Node} this This node
17344         * @param {Boolean} deep
17345         * @param {Boolean} anim
17346         */
17347         "beforeexpand" : true,
17348         /**
17349         * @event beforecollapse
17350         * Fires before this node is collapsed, return false to cancel.
17351         * @param {Node} this This node
17352         * @param {Boolean} deep
17353         * @param {Boolean} anim
17354         */
17355         "beforecollapse" : true,
17356         /**
17357         * @event expand
17358         * Fires when this node is expanded
17359         * @param {Node} this This node
17360         */
17361         "expand" : true,
17362         /**
17363         * @event disabledchange
17364         * Fires when the disabled status of this node changes
17365         * @param {Node} this This node
17366         * @param {Boolean} disabled
17367         */
17368         "disabledchange" : true,
17369         /**
17370         * @event collapse
17371         * Fires when this node is collapsed
17372         * @param {Node} this This node
17373         */
17374         "collapse" : true,
17375         /**
17376         * @event beforeclick
17377         * Fires before click processing. Return false to cancel the default action.
17378         * @param {Node} this This node
17379         * @param {Roo.EventObject} e The event object
17380         */
17381         "beforeclick":true,
17382         /**
17383         * @event checkchange
17384         * Fires when a node with a checkbox's checked property changes
17385         * @param {Node} this This node
17386         * @param {Boolean} checked
17387         */
17388         "checkchange":true,
17389         /**
17390         * @event click
17391         * Fires when this node is clicked
17392         * @param {Node} this This node
17393         * @param {Roo.EventObject} e The event object
17394         */
17395         "click":true,
17396         /**
17397         * @event dblclick
17398         * Fires when this node is double clicked
17399         * @param {Node} this This node
17400         * @param {Roo.EventObject} e The event object
17401         */
17402         "dblclick":true,
17403         /**
17404         * @event contextmenu
17405         * Fires when this node is right clicked
17406         * @param {Node} this This node
17407         * @param {Roo.EventObject} e The event object
17408         */
17409         "contextmenu":true,
17410         /**
17411         * @event beforechildrenrendered
17412         * Fires right before the child nodes for this node are rendered
17413         * @param {Node} this This node
17414         */
17415         "beforechildrenrendered":true
17416     });
17417
17418     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17419
17420     /**
17421      * Read-only. The UI for this node
17422      * @type TreeNodeUI
17423      */
17424     this.ui = new uiClass(this);
17425     
17426     // finally support items[]
17427     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17428         return;
17429     }
17430     
17431     
17432     Roo.each(this.attributes.items, function(c) {
17433         this.appendChild(Roo.factory(c,Roo.Tree));
17434     }, this);
17435     delete this.attributes.items;
17436     
17437     
17438     
17439 };
17440 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17441     preventHScroll: true,
17442     /**
17443      * Returns true if this node is expanded
17444      * @return {Boolean}
17445      */
17446     isExpanded : function(){
17447         return this.expanded;
17448     },
17449
17450     /**
17451      * Returns the UI object for this node
17452      * @return {TreeNodeUI}
17453      */
17454     getUI : function(){
17455         return this.ui;
17456     },
17457
17458     // private override
17459     setFirstChild : function(node){
17460         var of = this.firstChild;
17461         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17462         if(this.childrenRendered && of && node != of){
17463             of.renderIndent(true, true);
17464         }
17465         if(this.rendered){
17466             this.renderIndent(true, true);
17467         }
17468     },
17469
17470     // private override
17471     setLastChild : function(node){
17472         var ol = this.lastChild;
17473         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17474         if(this.childrenRendered && ol && node != ol){
17475             ol.renderIndent(true, true);
17476         }
17477         if(this.rendered){
17478             this.renderIndent(true, true);
17479         }
17480     },
17481
17482     // these methods are overridden to provide lazy rendering support
17483     // private override
17484     appendChild : function()
17485     {
17486         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17487         if(node && this.childrenRendered){
17488             node.render();
17489         }
17490         this.ui.updateExpandIcon();
17491         return node;
17492     },
17493
17494     // private override
17495     removeChild : function(node){
17496         this.ownerTree.getSelectionModel().unselect(node);
17497         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17498         // if it's been rendered remove dom node
17499         if(this.childrenRendered){
17500             node.ui.remove();
17501         }
17502         if(this.childNodes.length < 1){
17503             this.collapse(false, false);
17504         }else{
17505             this.ui.updateExpandIcon();
17506         }
17507         if(!this.firstChild) {
17508             this.childrenRendered = false;
17509         }
17510         return node;
17511     },
17512
17513     // private override
17514     insertBefore : function(node, refNode){
17515         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17516         if(newNode && refNode && this.childrenRendered){
17517             node.render();
17518         }
17519         this.ui.updateExpandIcon();
17520         return newNode;
17521     },
17522
17523     /**
17524      * Sets the text for this node
17525      * @param {String} text
17526      */
17527     setText : function(text){
17528         var oldText = this.text;
17529         this.text = text;
17530         this.attributes.text = text;
17531         if(this.rendered){ // event without subscribing
17532             this.ui.onTextChange(this, text, oldText);
17533         }
17534         this.fireEvent("textchange", this, text, oldText);
17535     },
17536
17537     /**
17538      * Triggers selection of this node
17539      */
17540     select : function(){
17541         this.getOwnerTree().getSelectionModel().select(this);
17542     },
17543
17544     /**
17545      * Triggers deselection of this node
17546      */
17547     unselect : function(){
17548         this.getOwnerTree().getSelectionModel().unselect(this);
17549     },
17550
17551     /**
17552      * Returns true if this node is selected
17553      * @return {Boolean}
17554      */
17555     isSelected : function(){
17556         return this.getOwnerTree().getSelectionModel().isSelected(this);
17557     },
17558
17559     /**
17560      * Expand this node.
17561      * @param {Boolean} deep (optional) True to expand all children as well
17562      * @param {Boolean} anim (optional) false to cancel the default animation
17563      * @param {Function} callback (optional) A callback to be called when
17564      * expanding this node completes (does not wait for deep expand to complete).
17565      * Called with 1 parameter, this node.
17566      */
17567     expand : function(deep, anim, callback){
17568         if(!this.expanded){
17569             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17570                 return;
17571             }
17572             if(!this.childrenRendered){
17573                 this.renderChildren();
17574             }
17575             this.expanded = true;
17576             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17577                 this.ui.animExpand(function(){
17578                     this.fireEvent("expand", this);
17579                     if(typeof callback == "function"){
17580                         callback(this);
17581                     }
17582                     if(deep === true){
17583                         this.expandChildNodes(true);
17584                     }
17585                 }.createDelegate(this));
17586                 return;
17587             }else{
17588                 this.ui.expand();
17589                 this.fireEvent("expand", this);
17590                 if(typeof callback == "function"){
17591                     callback(this);
17592                 }
17593             }
17594         }else{
17595            if(typeof callback == "function"){
17596                callback(this);
17597            }
17598         }
17599         if(deep === true){
17600             this.expandChildNodes(true);
17601         }
17602     },
17603
17604     isHiddenRoot : function(){
17605         return this.isRoot && !this.getOwnerTree().rootVisible;
17606     },
17607
17608     /**
17609      * Collapse this node.
17610      * @param {Boolean} deep (optional) True to collapse all children as well
17611      * @param {Boolean} anim (optional) false to cancel the default animation
17612      */
17613     collapse : function(deep, anim){
17614         if(this.expanded && !this.isHiddenRoot()){
17615             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17616                 return;
17617             }
17618             this.expanded = false;
17619             if((this.getOwnerTree().animate && anim !== false) || anim){
17620                 this.ui.animCollapse(function(){
17621                     this.fireEvent("collapse", this);
17622                     if(deep === true){
17623                         this.collapseChildNodes(true);
17624                     }
17625                 }.createDelegate(this));
17626                 return;
17627             }else{
17628                 this.ui.collapse();
17629                 this.fireEvent("collapse", this);
17630             }
17631         }
17632         if(deep === true){
17633             var cs = this.childNodes;
17634             for(var i = 0, len = cs.length; i < len; i++) {
17635                 cs[i].collapse(true, false);
17636             }
17637         }
17638     },
17639
17640     // private
17641     delayedExpand : function(delay){
17642         if(!this.expandProcId){
17643             this.expandProcId = this.expand.defer(delay, this);
17644         }
17645     },
17646
17647     // private
17648     cancelExpand : function(){
17649         if(this.expandProcId){
17650             clearTimeout(this.expandProcId);
17651         }
17652         this.expandProcId = false;
17653     },
17654
17655     /**
17656      * Toggles expanded/collapsed state of the node
17657      */
17658     toggle : function(){
17659         if(this.expanded){
17660             this.collapse();
17661         }else{
17662             this.expand();
17663         }
17664     },
17665
17666     /**
17667      * Ensures all parent nodes are expanded
17668      */
17669     ensureVisible : function(callback){
17670         var tree = this.getOwnerTree();
17671         tree.expandPath(this.parentNode.getPath(), false, function(){
17672             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17673             Roo.callback(callback);
17674         }.createDelegate(this));
17675     },
17676
17677     /**
17678      * Expand all child nodes
17679      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17680      */
17681     expandChildNodes : function(deep){
17682         var cs = this.childNodes;
17683         for(var i = 0, len = cs.length; i < len; i++) {
17684                 cs[i].expand(deep);
17685         }
17686     },
17687
17688     /**
17689      * Collapse all child nodes
17690      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17691      */
17692     collapseChildNodes : function(deep){
17693         var cs = this.childNodes;
17694         for(var i = 0, len = cs.length; i < len; i++) {
17695                 cs[i].collapse(deep);
17696         }
17697     },
17698
17699     /**
17700      * Disables this node
17701      */
17702     disable : function(){
17703         this.disabled = true;
17704         this.unselect();
17705         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17706             this.ui.onDisableChange(this, true);
17707         }
17708         this.fireEvent("disabledchange", this, true);
17709     },
17710
17711     /**
17712      * Enables this node
17713      */
17714     enable : function(){
17715         this.disabled = false;
17716         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17717             this.ui.onDisableChange(this, false);
17718         }
17719         this.fireEvent("disabledchange", this, false);
17720     },
17721
17722     // private
17723     renderChildren : function(suppressEvent){
17724         if(suppressEvent !== false){
17725             this.fireEvent("beforechildrenrendered", this);
17726         }
17727         var cs = this.childNodes;
17728         for(var i = 0, len = cs.length; i < len; i++){
17729             cs[i].render(true);
17730         }
17731         this.childrenRendered = true;
17732     },
17733
17734     // private
17735     sort : function(fn, scope){
17736         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17737         if(this.childrenRendered){
17738             var cs = this.childNodes;
17739             for(var i = 0, len = cs.length; i < len; i++){
17740                 cs[i].render(true);
17741             }
17742         }
17743     },
17744
17745     // private
17746     render : function(bulkRender){
17747         this.ui.render(bulkRender);
17748         if(!this.rendered){
17749             this.rendered = true;
17750             if(this.expanded){
17751                 this.expanded = false;
17752                 this.expand(false, false);
17753             }
17754         }
17755     },
17756
17757     // private
17758     renderIndent : function(deep, refresh){
17759         if(refresh){
17760             this.ui.childIndent = null;
17761         }
17762         this.ui.renderIndent();
17763         if(deep === true && this.childrenRendered){
17764             var cs = this.childNodes;
17765             for(var i = 0, len = cs.length; i < len; i++){
17766                 cs[i].renderIndent(true, refresh);
17767             }
17768         }
17769     }
17770 });/*
17771  * Based on:
17772  * Ext JS Library 1.1.1
17773  * Copyright(c) 2006-2007, Ext JS, LLC.
17774  *
17775  * Originally Released Under LGPL - original licence link has changed is not relivant.
17776  *
17777  * Fork - LGPL
17778  * <script type="text/javascript">
17779  */
17780  
17781 /**
17782  * @class Roo.tree.AsyncTreeNode
17783  * @extends Roo.tree.TreeNode
17784  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17785  * @constructor
17786  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17787  */
17788  Roo.tree.AsyncTreeNode = function(config){
17789     this.loaded = false;
17790     this.loading = false;
17791     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17792     /**
17793     * @event beforeload
17794     * Fires before this node is loaded, return false to cancel
17795     * @param {Node} this This node
17796     */
17797     this.addEvents({'beforeload':true, 'load': true});
17798     /**
17799     * @event load
17800     * Fires when this node is loaded
17801     * @param {Node} this This node
17802     */
17803     /**
17804      * The loader used by this node (defaults to using the tree's defined loader)
17805      * @type TreeLoader
17806      * @property loader
17807      */
17808 };
17809 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17810     expand : function(deep, anim, callback){
17811         if(this.loading){ // if an async load is already running, waiting til it's done
17812             var timer;
17813             var f = function(){
17814                 if(!this.loading){ // done loading
17815                     clearInterval(timer);
17816                     this.expand(deep, anim, callback);
17817                 }
17818             }.createDelegate(this);
17819             timer = setInterval(f, 200);
17820             return;
17821         }
17822         if(!this.loaded){
17823             if(this.fireEvent("beforeload", this) === false){
17824                 return;
17825             }
17826             this.loading = true;
17827             this.ui.beforeLoad(this);
17828             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17829             if(loader){
17830                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17831                 return;
17832             }
17833         }
17834         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17835     },
17836     
17837     /**
17838      * Returns true if this node is currently loading
17839      * @return {Boolean}
17840      */
17841     isLoading : function(){
17842         return this.loading;  
17843     },
17844     
17845     loadComplete : function(deep, anim, callback){
17846         this.loading = false;
17847         this.loaded = true;
17848         this.ui.afterLoad(this);
17849         this.fireEvent("load", this);
17850         this.expand(deep, anim, callback);
17851     },
17852     
17853     /**
17854      * Returns true if this node has been loaded
17855      * @return {Boolean}
17856      */
17857     isLoaded : function(){
17858         return this.loaded;
17859     },
17860     
17861     hasChildNodes : function(){
17862         if(!this.isLeaf() && !this.loaded){
17863             return true;
17864         }else{
17865             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17866         }
17867     },
17868
17869     /**
17870      * Trigger a reload for this node
17871      * @param {Function} callback
17872      */
17873     reload : function(callback){
17874         this.collapse(false, false);
17875         while(this.firstChild){
17876             this.removeChild(this.firstChild);
17877         }
17878         this.childrenRendered = false;
17879         this.loaded = false;
17880         if(this.isHiddenRoot()){
17881             this.expanded = false;
17882         }
17883         this.expand(false, false, callback);
17884     }
17885 });/*
17886  * Based on:
17887  * Ext JS Library 1.1.1
17888  * Copyright(c) 2006-2007, Ext JS, LLC.
17889  *
17890  * Originally Released Under LGPL - original licence link has changed is not relivant.
17891  *
17892  * Fork - LGPL
17893  * <script type="text/javascript">
17894  */
17895  
17896 /**
17897  * @class Roo.tree.TreeNodeUI
17898  * @constructor
17899  * @param {Object} node The node to render
17900  * The TreeNode UI implementation is separate from the
17901  * tree implementation. Unless you are customizing the tree UI,
17902  * you should never have to use this directly.
17903  */
17904 Roo.tree.TreeNodeUI = function(node){
17905     this.node = node;
17906     this.rendered = false;
17907     this.animating = false;
17908     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17909 };
17910
17911 Roo.tree.TreeNodeUI.prototype = {
17912     removeChild : function(node){
17913         if(this.rendered){
17914             this.ctNode.removeChild(node.ui.getEl());
17915         }
17916     },
17917
17918     beforeLoad : function(){
17919          this.addClass("x-tree-node-loading");
17920     },
17921
17922     afterLoad : function(){
17923          this.removeClass("x-tree-node-loading");
17924     },
17925
17926     onTextChange : function(node, text, oldText){
17927         if(this.rendered){
17928             this.textNode.innerHTML = text;
17929         }
17930     },
17931
17932     onDisableChange : function(node, state){
17933         this.disabled = state;
17934         if(state){
17935             this.addClass("x-tree-node-disabled");
17936         }else{
17937             this.removeClass("x-tree-node-disabled");
17938         }
17939     },
17940
17941     onSelectedChange : function(state){
17942         if(state){
17943             this.focus();
17944             this.addClass("x-tree-selected");
17945         }else{
17946             //this.blur();
17947             this.removeClass("x-tree-selected");
17948         }
17949     },
17950
17951     onMove : function(tree, node, oldParent, newParent, index, refNode){
17952         this.childIndent = null;
17953         if(this.rendered){
17954             var targetNode = newParent.ui.getContainer();
17955             if(!targetNode){//target not rendered
17956                 this.holder = document.createElement("div");
17957                 this.holder.appendChild(this.wrap);
17958                 return;
17959             }
17960             var insertBefore = refNode ? refNode.ui.getEl() : null;
17961             if(insertBefore){
17962                 targetNode.insertBefore(this.wrap, insertBefore);
17963             }else{
17964                 targetNode.appendChild(this.wrap);
17965             }
17966             this.node.renderIndent(true);
17967         }
17968     },
17969
17970     addClass : function(cls){
17971         if(this.elNode){
17972             Roo.fly(this.elNode).addClass(cls);
17973         }
17974     },
17975
17976     removeClass : function(cls){
17977         if(this.elNode){
17978             Roo.fly(this.elNode).removeClass(cls);
17979         }
17980     },
17981
17982     remove : function(){
17983         if(this.rendered){
17984             this.holder = document.createElement("div");
17985             this.holder.appendChild(this.wrap);
17986         }
17987     },
17988
17989     fireEvent : function(){
17990         return this.node.fireEvent.apply(this.node, arguments);
17991     },
17992
17993     initEvents : function(){
17994         this.node.on("move", this.onMove, this);
17995         var E = Roo.EventManager;
17996         var a = this.anchor;
17997
17998         var el = Roo.fly(a, '_treeui');
17999
18000         if(Roo.isOpera){ // opera render bug ignores the CSS
18001             el.setStyle("text-decoration", "none");
18002         }
18003
18004         el.on("click", this.onClick, this);
18005         el.on("dblclick", this.onDblClick, this);
18006
18007         if(this.checkbox){
18008             Roo.EventManager.on(this.checkbox,
18009                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18010         }
18011
18012         el.on("contextmenu", this.onContextMenu, this);
18013
18014         var icon = Roo.fly(this.iconNode);
18015         icon.on("click", this.onClick, this);
18016         icon.on("dblclick", this.onDblClick, this);
18017         icon.on("contextmenu", this.onContextMenu, this);
18018         E.on(this.ecNode, "click", this.ecClick, this, true);
18019
18020         if(this.node.disabled){
18021             this.addClass("x-tree-node-disabled");
18022         }
18023         if(this.node.hidden){
18024             this.addClass("x-tree-node-disabled");
18025         }
18026         var ot = this.node.getOwnerTree();
18027         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18028         if(dd && (!this.node.isRoot || ot.rootVisible)){
18029             Roo.dd.Registry.register(this.elNode, {
18030                 node: this.node,
18031                 handles: this.getDDHandles(),
18032                 isHandle: false
18033             });
18034         }
18035     },
18036
18037     getDDHandles : function(){
18038         return [this.iconNode, this.textNode];
18039     },
18040
18041     hide : function(){
18042         if(this.rendered){
18043             this.wrap.style.display = "none";
18044         }
18045     },
18046
18047     show : function(){
18048         if(this.rendered){
18049             this.wrap.style.display = "";
18050         }
18051     },
18052
18053     onContextMenu : function(e){
18054         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18055             e.preventDefault();
18056             this.focus();
18057             this.fireEvent("contextmenu", this.node, e);
18058         }
18059     },
18060
18061     onClick : function(e){
18062         if(this.dropping){
18063             e.stopEvent();
18064             return;
18065         }
18066         if(this.fireEvent("beforeclick", this.node, e) !== false){
18067             if(!this.disabled && this.node.attributes.href){
18068                 this.fireEvent("click", this.node, e);
18069                 return;
18070             }
18071             e.preventDefault();
18072             if(this.disabled){
18073                 return;
18074             }
18075
18076             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18077                 this.node.toggle();
18078             }
18079
18080             this.fireEvent("click", this.node, e);
18081         }else{
18082             e.stopEvent();
18083         }
18084     },
18085
18086     onDblClick : function(e){
18087         e.preventDefault();
18088         if(this.disabled){
18089             return;
18090         }
18091         if(this.checkbox){
18092             this.toggleCheck();
18093         }
18094         if(!this.animating && this.node.hasChildNodes()){
18095             this.node.toggle();
18096         }
18097         this.fireEvent("dblclick", this.node, e);
18098     },
18099
18100     onCheckChange : function(){
18101         var checked = this.checkbox.checked;
18102         this.node.attributes.checked = checked;
18103         this.fireEvent('checkchange', this.node, checked);
18104     },
18105
18106     ecClick : function(e){
18107         if(!this.animating && this.node.hasChildNodes()){
18108             this.node.toggle();
18109         }
18110     },
18111
18112     startDrop : function(){
18113         this.dropping = true;
18114     },
18115
18116     // delayed drop so the click event doesn't get fired on a drop
18117     endDrop : function(){
18118        setTimeout(function(){
18119            this.dropping = false;
18120        }.createDelegate(this), 50);
18121     },
18122
18123     expand : function(){
18124         this.updateExpandIcon();
18125         this.ctNode.style.display = "";
18126     },
18127
18128     focus : function(){
18129         if(!this.node.preventHScroll){
18130             try{this.anchor.focus();
18131             }catch(e){}
18132         }else if(!Roo.isIE){
18133             try{
18134                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18135                 var l = noscroll.scrollLeft;
18136                 this.anchor.focus();
18137                 noscroll.scrollLeft = l;
18138             }catch(e){}
18139         }
18140     },
18141
18142     toggleCheck : function(value){
18143         var cb = this.checkbox;
18144         if(cb){
18145             cb.checked = (value === undefined ? !cb.checked : value);
18146         }
18147     },
18148
18149     blur : function(){
18150         try{
18151             this.anchor.blur();
18152         }catch(e){}
18153     },
18154
18155     animExpand : function(callback){
18156         var ct = Roo.get(this.ctNode);
18157         ct.stopFx();
18158         if(!this.node.hasChildNodes()){
18159             this.updateExpandIcon();
18160             this.ctNode.style.display = "";
18161             Roo.callback(callback);
18162             return;
18163         }
18164         this.animating = true;
18165         this.updateExpandIcon();
18166
18167         ct.slideIn('t', {
18168            callback : function(){
18169                this.animating = false;
18170                Roo.callback(callback);
18171             },
18172             scope: this,
18173             duration: this.node.ownerTree.duration || .25
18174         });
18175     },
18176
18177     highlight : function(){
18178         var tree = this.node.getOwnerTree();
18179         Roo.fly(this.wrap).highlight(
18180             tree.hlColor || "C3DAF9",
18181             {endColor: tree.hlBaseColor}
18182         );
18183     },
18184
18185     collapse : function(){
18186         this.updateExpandIcon();
18187         this.ctNode.style.display = "none";
18188     },
18189
18190     animCollapse : function(callback){
18191         var ct = Roo.get(this.ctNode);
18192         ct.enableDisplayMode('block');
18193         ct.stopFx();
18194
18195         this.animating = true;
18196         this.updateExpandIcon();
18197
18198         ct.slideOut('t', {
18199             callback : function(){
18200                this.animating = false;
18201                Roo.callback(callback);
18202             },
18203             scope: this,
18204             duration: this.node.ownerTree.duration || .25
18205         });
18206     },
18207
18208     getContainer : function(){
18209         return this.ctNode;
18210     },
18211
18212     getEl : function(){
18213         return this.wrap;
18214     },
18215
18216     appendDDGhost : function(ghostNode){
18217         ghostNode.appendChild(this.elNode.cloneNode(true));
18218     },
18219
18220     getDDRepairXY : function(){
18221         return Roo.lib.Dom.getXY(this.iconNode);
18222     },
18223
18224     onRender : function(){
18225         this.render();
18226     },
18227
18228     render : function(bulkRender){
18229         var n = this.node, a = n.attributes;
18230         var targetNode = n.parentNode ?
18231               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18232
18233         if(!this.rendered){
18234             this.rendered = true;
18235
18236             this.renderElements(n, a, targetNode, bulkRender);
18237
18238             if(a.qtip){
18239                if(this.textNode.setAttributeNS){
18240                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18241                    if(a.qtipTitle){
18242                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18243                    }
18244                }else{
18245                    this.textNode.setAttribute("ext:qtip", a.qtip);
18246                    if(a.qtipTitle){
18247                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18248                    }
18249                }
18250             }else if(a.qtipCfg){
18251                 a.qtipCfg.target = Roo.id(this.textNode);
18252                 Roo.QuickTips.register(a.qtipCfg);
18253             }
18254             this.initEvents();
18255             if(!this.node.expanded){
18256                 this.updateExpandIcon();
18257             }
18258         }else{
18259             if(bulkRender === true) {
18260                 targetNode.appendChild(this.wrap);
18261             }
18262         }
18263     },
18264
18265     renderElements : function(n, a, targetNode, bulkRender)
18266     {
18267         // add some indent caching, this helps performance when rendering a large tree
18268         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18269         var t = n.getOwnerTree();
18270         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18271         if (typeof(n.attributes.html) != 'undefined') {
18272             txt = n.attributes.html;
18273         }
18274         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18275         var cb = typeof a.checked == 'boolean';
18276         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18277         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18278             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18279             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18280             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18281             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18282             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18283              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18284                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18285             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18286             "</li>"];
18287
18288         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18289             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18290                                 n.nextSibling.ui.getEl(), buf.join(""));
18291         }else{
18292             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18293         }
18294
18295         this.elNode = this.wrap.childNodes[0];
18296         this.ctNode = this.wrap.childNodes[1];
18297         var cs = this.elNode.childNodes;
18298         this.indentNode = cs[0];
18299         this.ecNode = cs[1];
18300         this.iconNode = cs[2];
18301         var index = 3;
18302         if(cb){
18303             this.checkbox = cs[3];
18304             index++;
18305         }
18306         this.anchor = cs[index];
18307         this.textNode = cs[index].firstChild;
18308     },
18309
18310     getAnchor : function(){
18311         return this.anchor;
18312     },
18313
18314     getTextEl : function(){
18315         return this.textNode;
18316     },
18317
18318     getIconEl : function(){
18319         return this.iconNode;
18320     },
18321
18322     isChecked : function(){
18323         return this.checkbox ? this.checkbox.checked : false;
18324     },
18325
18326     updateExpandIcon : function(){
18327         if(this.rendered){
18328             var n = this.node, c1, c2;
18329             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18330             var hasChild = n.hasChildNodes();
18331             if(hasChild){
18332                 if(n.expanded){
18333                     cls += "-minus";
18334                     c1 = "x-tree-node-collapsed";
18335                     c2 = "x-tree-node-expanded";
18336                 }else{
18337                     cls += "-plus";
18338                     c1 = "x-tree-node-expanded";
18339                     c2 = "x-tree-node-collapsed";
18340                 }
18341                 if(this.wasLeaf){
18342                     this.removeClass("x-tree-node-leaf");
18343                     this.wasLeaf = false;
18344                 }
18345                 if(this.c1 != c1 || this.c2 != c2){
18346                     Roo.fly(this.elNode).replaceClass(c1, c2);
18347                     this.c1 = c1; this.c2 = c2;
18348                 }
18349             }else{
18350                 // this changes non-leafs into leafs if they have no children.
18351                 // it's not very rational behaviour..
18352                 
18353                 if(!this.wasLeaf && this.node.leaf){
18354                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18355                     delete this.c1;
18356                     delete this.c2;
18357                     this.wasLeaf = true;
18358                 }
18359             }
18360             var ecc = "x-tree-ec-icon "+cls;
18361             if(this.ecc != ecc){
18362                 this.ecNode.className = ecc;
18363                 this.ecc = ecc;
18364             }
18365         }
18366     },
18367
18368     getChildIndent : function(){
18369         if(!this.childIndent){
18370             var buf = [];
18371             var p = this.node;
18372             while(p){
18373                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18374                     if(!p.isLast()) {
18375                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18376                     } else {
18377                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18378                     }
18379                 }
18380                 p = p.parentNode;
18381             }
18382             this.childIndent = buf.join("");
18383         }
18384         return this.childIndent;
18385     },
18386
18387     renderIndent : function(){
18388         if(this.rendered){
18389             var indent = "";
18390             var p = this.node.parentNode;
18391             if(p){
18392                 indent = p.ui.getChildIndent();
18393             }
18394             if(this.indentMarkup != indent){ // don't rerender if not required
18395                 this.indentNode.innerHTML = indent;
18396                 this.indentMarkup = indent;
18397             }
18398             this.updateExpandIcon();
18399         }
18400     }
18401 };
18402
18403 Roo.tree.RootTreeNodeUI = function(){
18404     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18405 };
18406 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18407     render : function(){
18408         if(!this.rendered){
18409             var targetNode = this.node.ownerTree.innerCt.dom;
18410             this.node.expanded = true;
18411             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18412             this.wrap = this.ctNode = targetNode.firstChild;
18413         }
18414     },
18415     collapse : function(){
18416     },
18417     expand : function(){
18418     }
18419 });/*
18420  * Based on:
18421  * Ext JS Library 1.1.1
18422  * Copyright(c) 2006-2007, Ext JS, LLC.
18423  *
18424  * Originally Released Under LGPL - original licence link has changed is not relivant.
18425  *
18426  * Fork - LGPL
18427  * <script type="text/javascript">
18428  */
18429 /**
18430  * @class Roo.tree.TreeLoader
18431  * @extends Roo.util.Observable
18432  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18433  * nodes from a specified URL. The response must be a javascript Array definition
18434  * who's elements are node definition objects. eg:
18435  * <pre><code>
18436 {  success : true,
18437    data :      [
18438    
18439     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18440     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18441     ]
18442 }
18443
18444
18445 </code></pre>
18446  * <br><br>
18447  * The old style respose with just an array is still supported, but not recommended.
18448  * <br><br>
18449  *
18450  * A server request is sent, and child nodes are loaded only when a node is expanded.
18451  * The loading node's id is passed to the server under the parameter name "node" to
18452  * enable the server to produce the correct child nodes.
18453  * <br><br>
18454  * To pass extra parameters, an event handler may be attached to the "beforeload"
18455  * event, and the parameters specified in the TreeLoader's baseParams property:
18456  * <pre><code>
18457     myTreeLoader.on("beforeload", function(treeLoader, node) {
18458         this.baseParams.category = node.attributes.category;
18459     }, this);
18460 </code></pre><
18461  * This would pass an HTTP parameter called "category" to the server containing
18462  * the value of the Node's "category" attribute.
18463  * @constructor
18464  * Creates a new Treeloader.
18465  * @param {Object} config A config object containing config properties.
18466  */
18467 Roo.tree.TreeLoader = function(config){
18468     this.baseParams = {};
18469     this.requestMethod = "POST";
18470     Roo.apply(this, config);
18471
18472     this.addEvents({
18473     
18474         /**
18475          * @event beforeload
18476          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18477          * @param {Object} This TreeLoader object.
18478          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18479          * @param {Object} callback The callback function specified in the {@link #load} call.
18480          */
18481         beforeload : true,
18482         /**
18483          * @event load
18484          * Fires when the node has been successfuly loaded.
18485          * @param {Object} This TreeLoader object.
18486          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18487          * @param {Object} response The response object containing the data from the server.
18488          */
18489         load : true,
18490         /**
18491          * @event loadexception
18492          * Fires if the network request failed.
18493          * @param {Object} This TreeLoader object.
18494          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18495          * @param {Object} response The response object containing the data from the server.
18496          */
18497         loadexception : true,
18498         /**
18499          * @event create
18500          * Fires before a node is created, enabling you to return custom Node types 
18501          * @param {Object} This TreeLoader object.
18502          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18503          */
18504         create : true
18505     });
18506
18507     Roo.tree.TreeLoader.superclass.constructor.call(this);
18508 };
18509
18510 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18511     /**
18512     * @cfg {String} dataUrl The URL from which to request a Json string which
18513     * specifies an array of node definition object representing the child nodes
18514     * to be loaded.
18515     */
18516     /**
18517     * @cfg {Object} baseParams (optional) An object containing properties which
18518     * specify HTTP parameters to be passed to each request for child nodes.
18519     */
18520     /**
18521     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18522     * created by this loader. If the attributes sent by the server have an attribute in this object,
18523     * they take priority.
18524     */
18525     /**
18526     * @cfg {Object} uiProviders (optional) An object containing properties which
18527     * 
18528     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18529     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18530     * <i>uiProvider</i> attribute of a returned child node is a string rather
18531     * than a reference to a TreeNodeUI implementation, this that string value
18532     * is used as a property name in the uiProviders object. You can define the provider named
18533     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18534     */
18535     uiProviders : {},
18536
18537     /**
18538     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18539     * child nodes before loading.
18540     */
18541     clearOnLoad : true,
18542
18543     /**
18544     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18545     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18546     * Grid query { data : [ .....] }
18547     */
18548     
18549     root : false,
18550      /**
18551     * @cfg {String} queryParam (optional) 
18552     * Name of the query as it will be passed on the querystring (defaults to 'node')
18553     * eg. the request will be ?node=[id]
18554     */
18555     
18556     
18557     queryParam: false,
18558     
18559     /**
18560      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18561      * This is called automatically when a node is expanded, but may be used to reload
18562      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18563      * @param {Roo.tree.TreeNode} node
18564      * @param {Function} callback
18565      */
18566     load : function(node, callback){
18567         if(this.clearOnLoad){
18568             while(node.firstChild){
18569                 node.removeChild(node.firstChild);
18570             }
18571         }
18572         if(node.attributes.children){ // preloaded json children
18573             var cs = node.attributes.children;
18574             for(var i = 0, len = cs.length; i < len; i++){
18575                 node.appendChild(this.createNode(cs[i]));
18576             }
18577             if(typeof callback == "function"){
18578                 callback();
18579             }
18580         }else if(this.dataUrl){
18581             this.requestData(node, callback);
18582         }
18583     },
18584
18585     getParams: function(node){
18586         var buf = [], bp = this.baseParams;
18587         for(var key in bp){
18588             if(typeof bp[key] != "function"){
18589                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18590             }
18591         }
18592         var n = this.queryParam === false ? 'node' : this.queryParam;
18593         buf.push(n + "=", encodeURIComponent(node.id));
18594         return buf.join("");
18595     },
18596
18597     requestData : function(node, callback){
18598         if(this.fireEvent("beforeload", this, node, callback) !== false){
18599             this.transId = Roo.Ajax.request({
18600                 method:this.requestMethod,
18601                 url: this.dataUrl||this.url,
18602                 success: this.handleResponse,
18603                 failure: this.handleFailure,
18604                 scope: this,
18605                 argument: {callback: callback, node: node},
18606                 params: this.getParams(node)
18607             });
18608         }else{
18609             // if the load is cancelled, make sure we notify
18610             // the node that we are done
18611             if(typeof callback == "function"){
18612                 callback();
18613             }
18614         }
18615     },
18616
18617     isLoading : function(){
18618         return this.transId ? true : false;
18619     },
18620
18621     abort : function(){
18622         if(this.isLoading()){
18623             Roo.Ajax.abort(this.transId);
18624         }
18625     },
18626
18627     // private
18628     createNode : function(attr)
18629     {
18630         // apply baseAttrs, nice idea Corey!
18631         if(this.baseAttrs){
18632             Roo.applyIf(attr, this.baseAttrs);
18633         }
18634         if(this.applyLoader !== false){
18635             attr.loader = this;
18636         }
18637         // uiProvider = depreciated..
18638         
18639         if(typeof(attr.uiProvider) == 'string'){
18640            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18641                 /**  eval:var:attr */ eval(attr.uiProvider);
18642         }
18643         if(typeof(this.uiProviders['default']) != 'undefined') {
18644             attr.uiProvider = this.uiProviders['default'];
18645         }
18646         
18647         this.fireEvent('create', this, attr);
18648         
18649         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18650         return(attr.leaf ?
18651                         new Roo.tree.TreeNode(attr) :
18652                         new Roo.tree.AsyncTreeNode(attr));
18653     },
18654
18655     processResponse : function(response, node, callback)
18656     {
18657         var json = response.responseText;
18658         try {
18659             
18660             var o = Roo.decode(json);
18661             
18662             if (this.root === false && typeof(o.success) != undefined) {
18663                 this.root = 'data'; // the default behaviour for list like data..
18664                 }
18665                 
18666             if (this.root !== false &&  !o.success) {
18667                 // it's a failure condition.
18668                 var a = response.argument;
18669                 this.fireEvent("loadexception", this, a.node, response);
18670                 Roo.log("Load failed - should have a handler really");
18671                 return;
18672             }
18673             
18674             
18675             
18676             if (this.root !== false) {
18677                  o = o[this.root];
18678             }
18679             
18680             for(var i = 0, len = o.length; i < len; i++){
18681                 var n = this.createNode(o[i]);
18682                 if(n){
18683                     node.appendChild(n);
18684                 }
18685             }
18686             if(typeof callback == "function"){
18687                 callback(this, node);
18688             }
18689         }catch(e){
18690             this.handleFailure(response);
18691         }
18692     },
18693
18694     handleResponse : function(response){
18695         this.transId = false;
18696         var a = response.argument;
18697         this.processResponse(response, a.node, a.callback);
18698         this.fireEvent("load", this, a.node, response);
18699     },
18700
18701     handleFailure : function(response)
18702     {
18703         // should handle failure better..
18704         this.transId = false;
18705         var a = response.argument;
18706         this.fireEvent("loadexception", this, a.node, response);
18707         if(typeof a.callback == "function"){
18708             a.callback(this, a.node);
18709         }
18710     }
18711 });/*
18712  * Based on:
18713  * Ext JS Library 1.1.1
18714  * Copyright(c) 2006-2007, Ext JS, LLC.
18715  *
18716  * Originally Released Under LGPL - original licence link has changed is not relivant.
18717  *
18718  * Fork - LGPL
18719  * <script type="text/javascript">
18720  */
18721
18722 /**
18723 * @class Roo.tree.TreeFilter
18724 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18725 * @param {TreePanel} tree
18726 * @param {Object} config (optional)
18727  */
18728 Roo.tree.TreeFilter = function(tree, config){
18729     this.tree = tree;
18730     this.filtered = {};
18731     Roo.apply(this, config);
18732 };
18733
18734 Roo.tree.TreeFilter.prototype = {
18735     clearBlank:false,
18736     reverse:false,
18737     autoClear:false,
18738     remove:false,
18739
18740      /**
18741      * Filter the data by a specific attribute.
18742      * @param {String/RegExp} value Either string that the attribute value
18743      * should start with or a RegExp to test against the attribute
18744      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18745      * @param {TreeNode} startNode (optional) The node to start the filter at.
18746      */
18747     filter : function(value, attr, startNode){
18748         attr = attr || "text";
18749         var f;
18750         if(typeof value == "string"){
18751             var vlen = value.length;
18752             // auto clear empty filter
18753             if(vlen == 0 && this.clearBlank){
18754                 this.clear();
18755                 return;
18756             }
18757             value = value.toLowerCase();
18758             f = function(n){
18759                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18760             };
18761         }else if(value.exec){ // regex?
18762             f = function(n){
18763                 return value.test(n.attributes[attr]);
18764             };
18765         }else{
18766             throw 'Illegal filter type, must be string or regex';
18767         }
18768         this.filterBy(f, null, startNode);
18769         },
18770
18771     /**
18772      * Filter by a function. The passed function will be called with each
18773      * node in the tree (or from the startNode). If the function returns true, the node is kept
18774      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18775      * @param {Function} fn The filter function
18776      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18777      */
18778     filterBy : function(fn, scope, startNode){
18779         startNode = startNode || this.tree.root;
18780         if(this.autoClear){
18781             this.clear();
18782         }
18783         var af = this.filtered, rv = this.reverse;
18784         var f = function(n){
18785             if(n == startNode){
18786                 return true;
18787             }
18788             if(af[n.id]){
18789                 return false;
18790             }
18791             var m = fn.call(scope || n, n);
18792             if(!m || rv){
18793                 af[n.id] = n;
18794                 n.ui.hide();
18795                 return false;
18796             }
18797             return true;
18798         };
18799         startNode.cascade(f);
18800         if(this.remove){
18801            for(var id in af){
18802                if(typeof id != "function"){
18803                    var n = af[id];
18804                    if(n && n.parentNode){
18805                        n.parentNode.removeChild(n);
18806                    }
18807                }
18808            }
18809         }
18810     },
18811
18812     /**
18813      * Clears the current filter. Note: with the "remove" option
18814      * set a filter cannot be cleared.
18815      */
18816     clear : function(){
18817         var t = this.tree;
18818         var af = this.filtered;
18819         for(var id in af){
18820             if(typeof id != "function"){
18821                 var n = af[id];
18822                 if(n){
18823                     n.ui.show();
18824                 }
18825             }
18826         }
18827         this.filtered = {};
18828     }
18829 };
18830 /*
18831  * Based on:
18832  * Ext JS Library 1.1.1
18833  * Copyright(c) 2006-2007, Ext JS, LLC.
18834  *
18835  * Originally Released Under LGPL - original licence link has changed is not relivant.
18836  *
18837  * Fork - LGPL
18838  * <script type="text/javascript">
18839  */
18840  
18841
18842 /**
18843  * @class Roo.tree.TreeSorter
18844  * Provides sorting of nodes in a TreePanel
18845  * 
18846  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18847  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18848  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18849  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18850  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18851  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18852  * @constructor
18853  * @param {TreePanel} tree
18854  * @param {Object} config
18855  */
18856 Roo.tree.TreeSorter = function(tree, config){
18857     Roo.apply(this, config);
18858     tree.on("beforechildrenrendered", this.doSort, this);
18859     tree.on("append", this.updateSort, this);
18860     tree.on("insert", this.updateSort, this);
18861     
18862     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18863     var p = this.property || "text";
18864     var sortType = this.sortType;
18865     var fs = this.folderSort;
18866     var cs = this.caseSensitive === true;
18867     var leafAttr = this.leafAttr || 'leaf';
18868
18869     this.sortFn = function(n1, n2){
18870         if(fs){
18871             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18872                 return 1;
18873             }
18874             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18875                 return -1;
18876             }
18877         }
18878         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18879         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18880         if(v1 < v2){
18881                         return dsc ? +1 : -1;
18882                 }else if(v1 > v2){
18883                         return dsc ? -1 : +1;
18884         }else{
18885                 return 0;
18886         }
18887     };
18888 };
18889
18890 Roo.tree.TreeSorter.prototype = {
18891     doSort : function(node){
18892         node.sort(this.sortFn);
18893     },
18894     
18895     compareNodes : function(n1, n2){
18896         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18897     },
18898     
18899     updateSort : function(tree, node){
18900         if(node.childrenRendered){
18901             this.doSort.defer(1, this, [node]);
18902         }
18903     }
18904 };/*
18905  * Based on:
18906  * Ext JS Library 1.1.1
18907  * Copyright(c) 2006-2007, Ext JS, LLC.
18908  *
18909  * Originally Released Under LGPL - original licence link has changed is not relivant.
18910  *
18911  * Fork - LGPL
18912  * <script type="text/javascript">
18913  */
18914
18915 if(Roo.dd.DropZone){
18916     
18917 Roo.tree.TreeDropZone = function(tree, config){
18918     this.allowParentInsert = false;
18919     this.allowContainerDrop = false;
18920     this.appendOnly = false;
18921     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18922     this.tree = tree;
18923     this.lastInsertClass = "x-tree-no-status";
18924     this.dragOverData = {};
18925 };
18926
18927 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18928     ddGroup : "TreeDD",
18929     
18930     expandDelay : 1000,
18931     
18932     expandNode : function(node){
18933         if(node.hasChildNodes() && !node.isExpanded()){
18934             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18935         }
18936     },
18937     
18938     queueExpand : function(node){
18939         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18940     },
18941     
18942     cancelExpand : function(){
18943         if(this.expandProcId){
18944             clearTimeout(this.expandProcId);
18945             this.expandProcId = false;
18946         }
18947     },
18948     
18949     isValidDropPoint : function(n, pt, dd, e, data){
18950         if(!n || !data){ return false; }
18951         var targetNode = n.node;
18952         var dropNode = data.node;
18953         // default drop rules
18954         if(!(targetNode && targetNode.isTarget && pt)){
18955             return false;
18956         }
18957         if(pt == "append" && targetNode.allowChildren === false){
18958             return false;
18959         }
18960         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18961             return false;
18962         }
18963         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18964             return false;
18965         }
18966         // reuse the object
18967         var overEvent = this.dragOverData;
18968         overEvent.tree = this.tree;
18969         overEvent.target = targetNode;
18970         overEvent.data = data;
18971         overEvent.point = pt;
18972         overEvent.source = dd;
18973         overEvent.rawEvent = e;
18974         overEvent.dropNode = dropNode;
18975         overEvent.cancel = false;  
18976         var result = this.tree.fireEvent("nodedragover", overEvent);
18977         return overEvent.cancel === false && result !== false;
18978     },
18979     
18980     getDropPoint : function(e, n, dd){
18981         var tn = n.node;
18982         if(tn.isRoot){
18983             return tn.allowChildren !== false ? "append" : false; // always append for root
18984         }
18985         var dragEl = n.ddel;
18986         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18987         var y = Roo.lib.Event.getPageY(e);
18988         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18989         
18990         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18991         var noAppend = tn.allowChildren === false;
18992         if(this.appendOnly || tn.parentNode.allowChildren === false){
18993             return noAppend ? false : "append";
18994         }
18995         var noBelow = false;
18996         if(!this.allowParentInsert){
18997             noBelow = tn.hasChildNodes() && tn.isExpanded();
18998         }
18999         var q = (b - t) / (noAppend ? 2 : 3);
19000         if(y >= t && y < (t + q)){
19001             return "above";
19002         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19003             return "below";
19004         }else{
19005             return "append";
19006         }
19007     },
19008     
19009     onNodeEnter : function(n, dd, e, data){
19010         this.cancelExpand();
19011     },
19012     
19013     onNodeOver : function(n, dd, e, data){
19014         var pt = this.getDropPoint(e, n, dd);
19015         var node = n.node;
19016         
19017         // auto node expand check
19018         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19019             this.queueExpand(node);
19020         }else if(pt != "append"){
19021             this.cancelExpand();
19022         }
19023         
19024         // set the insert point style on the target node
19025         var returnCls = this.dropNotAllowed;
19026         if(this.isValidDropPoint(n, pt, dd, e, data)){
19027            if(pt){
19028                var el = n.ddel;
19029                var cls;
19030                if(pt == "above"){
19031                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19032                    cls = "x-tree-drag-insert-above";
19033                }else if(pt == "below"){
19034                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19035                    cls = "x-tree-drag-insert-below";
19036                }else{
19037                    returnCls = "x-tree-drop-ok-append";
19038                    cls = "x-tree-drag-append";
19039                }
19040                if(this.lastInsertClass != cls){
19041                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19042                    this.lastInsertClass = cls;
19043                }
19044            }
19045        }
19046        return returnCls;
19047     },
19048     
19049     onNodeOut : function(n, dd, e, data){
19050         this.cancelExpand();
19051         this.removeDropIndicators(n);
19052     },
19053     
19054     onNodeDrop : function(n, dd, e, data){
19055         var point = this.getDropPoint(e, n, dd);
19056         var targetNode = n.node;
19057         targetNode.ui.startDrop();
19058         if(!this.isValidDropPoint(n, point, dd, e, data)){
19059             targetNode.ui.endDrop();
19060             return false;
19061         }
19062         // first try to find the drop node
19063         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19064         var dropEvent = {
19065             tree : this.tree,
19066             target: targetNode,
19067             data: data,
19068             point: point,
19069             source: dd,
19070             rawEvent: e,
19071             dropNode: dropNode,
19072             cancel: !dropNode   
19073         };
19074         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19075         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19076             targetNode.ui.endDrop();
19077             return false;
19078         }
19079         // allow target changing
19080         targetNode = dropEvent.target;
19081         if(point == "append" && !targetNode.isExpanded()){
19082             targetNode.expand(false, null, function(){
19083                 this.completeDrop(dropEvent);
19084             }.createDelegate(this));
19085         }else{
19086             this.completeDrop(dropEvent);
19087         }
19088         return true;
19089     },
19090     
19091     completeDrop : function(de){
19092         var ns = de.dropNode, p = de.point, t = de.target;
19093         if(!(ns instanceof Array)){
19094             ns = [ns];
19095         }
19096         var n;
19097         for(var i = 0, len = ns.length; i < len; i++){
19098             n = ns[i];
19099             if(p == "above"){
19100                 t.parentNode.insertBefore(n, t);
19101             }else if(p == "below"){
19102                 t.parentNode.insertBefore(n, t.nextSibling);
19103             }else{
19104                 t.appendChild(n);
19105             }
19106         }
19107         n.ui.focus();
19108         if(this.tree.hlDrop){
19109             n.ui.highlight();
19110         }
19111         t.ui.endDrop();
19112         this.tree.fireEvent("nodedrop", de);
19113     },
19114     
19115     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19116         if(this.tree.hlDrop){
19117             dropNode.ui.focus();
19118             dropNode.ui.highlight();
19119         }
19120         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19121     },
19122     
19123     getTree : function(){
19124         return this.tree;
19125     },
19126     
19127     removeDropIndicators : function(n){
19128         if(n && n.ddel){
19129             var el = n.ddel;
19130             Roo.fly(el).removeClass([
19131                     "x-tree-drag-insert-above",
19132                     "x-tree-drag-insert-below",
19133                     "x-tree-drag-append"]);
19134             this.lastInsertClass = "_noclass";
19135         }
19136     },
19137     
19138     beforeDragDrop : function(target, e, id){
19139         this.cancelExpand();
19140         return true;
19141     },
19142     
19143     afterRepair : function(data){
19144         if(data && Roo.enableFx){
19145             data.node.ui.highlight();
19146         }
19147         this.hideProxy();
19148     }    
19149 });
19150
19151 }
19152 /*
19153  * Based on:
19154  * Ext JS Library 1.1.1
19155  * Copyright(c) 2006-2007, Ext JS, LLC.
19156  *
19157  * Originally Released Under LGPL - original licence link has changed is not relivant.
19158  *
19159  * Fork - LGPL
19160  * <script type="text/javascript">
19161  */
19162  
19163
19164 if(Roo.dd.DragZone){
19165 Roo.tree.TreeDragZone = function(tree, config){
19166     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19167     this.tree = tree;
19168 };
19169
19170 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19171     ddGroup : "TreeDD",
19172     
19173     onBeforeDrag : function(data, e){
19174         var n = data.node;
19175         return n && n.draggable && !n.disabled;
19176     },
19177     
19178     onInitDrag : function(e){
19179         var data = this.dragData;
19180         this.tree.getSelectionModel().select(data.node);
19181         this.proxy.update("");
19182         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19183         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19184     },
19185     
19186     getRepairXY : function(e, data){
19187         return data.node.ui.getDDRepairXY();
19188     },
19189     
19190     onEndDrag : function(data, e){
19191         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19192     },
19193     
19194     onValidDrop : function(dd, e, id){
19195         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19196         this.hideProxy();
19197     },
19198     
19199     beforeInvalidDrop : function(e, id){
19200         // this scrolls the original position back into view
19201         var sm = this.tree.getSelectionModel();
19202         sm.clearSelections();
19203         sm.select(this.dragData.node);
19204     }
19205 });
19206 }/*
19207  * Based on:
19208  * Ext JS Library 1.1.1
19209  * Copyright(c) 2006-2007, Ext JS, LLC.
19210  *
19211  * Originally Released Under LGPL - original licence link has changed is not relivant.
19212  *
19213  * Fork - LGPL
19214  * <script type="text/javascript">
19215  */
19216 /**
19217  * @class Roo.tree.TreeEditor
19218  * @extends Roo.Editor
19219  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19220  * as the editor field.
19221  * @constructor
19222  * @param {Object} config (used to be the tree panel.)
19223  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19224  * 
19225  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19226  * @cfg {Roo.form.TextField|Object} field The field configuration
19227  *
19228  * 
19229  */
19230 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19231     var tree = config;
19232     var field;
19233     if (oldconfig) { // old style..
19234         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19235     } else {
19236         // new style..
19237         tree = config.tree;
19238         config.field = config.field  || {};
19239         config.field.xtype = 'TextField';
19240         field = Roo.factory(config.field, Roo.form);
19241     }
19242     config = config || {};
19243     
19244     
19245     this.addEvents({
19246         /**
19247          * @event beforenodeedit
19248          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19249          * false from the handler of this event.
19250          * @param {Editor} this
19251          * @param {Roo.tree.Node} node 
19252          */
19253         "beforenodeedit" : true
19254     });
19255     
19256     //Roo.log(config);
19257     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19258
19259     this.tree = tree;
19260
19261     tree.on('beforeclick', this.beforeNodeClick, this);
19262     tree.getTreeEl().on('mousedown', this.hide, this);
19263     this.on('complete', this.updateNode, this);
19264     this.on('beforestartedit', this.fitToTree, this);
19265     this.on('startedit', this.bindScroll, this, {delay:10});
19266     this.on('specialkey', this.onSpecialKey, this);
19267 };
19268
19269 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19270     /**
19271      * @cfg {String} alignment
19272      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19273      */
19274     alignment: "l-l",
19275     // inherit
19276     autoSize: false,
19277     /**
19278      * @cfg {Boolean} hideEl
19279      * True to hide the bound element while the editor is displayed (defaults to false)
19280      */
19281     hideEl : false,
19282     /**
19283      * @cfg {String} cls
19284      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19285      */
19286     cls: "x-small-editor x-tree-editor",
19287     /**
19288      * @cfg {Boolean} shim
19289      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19290      */
19291     shim:false,
19292     // inherit
19293     shadow:"frame",
19294     /**
19295      * @cfg {Number} maxWidth
19296      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19297      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19298      * scroll and client offsets into account prior to each edit.
19299      */
19300     maxWidth: 250,
19301
19302     editDelay : 350,
19303
19304     // private
19305     fitToTree : function(ed, el){
19306         var td = this.tree.getTreeEl().dom, nd = el.dom;
19307         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19308             td.scrollLeft = nd.offsetLeft;
19309         }
19310         var w = Math.min(
19311                 this.maxWidth,
19312                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19313         this.setSize(w, '');
19314         
19315         return this.fireEvent('beforenodeedit', this, this.editNode);
19316         
19317     },
19318
19319     // private
19320     triggerEdit : function(node){
19321         this.completeEdit();
19322         this.editNode = node;
19323         this.startEdit(node.ui.textNode, node.text);
19324     },
19325
19326     // private
19327     bindScroll : function(){
19328         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19329     },
19330
19331     // private
19332     beforeNodeClick : function(node, e){
19333         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19334         this.lastClick = new Date();
19335         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19336             e.stopEvent();
19337             this.triggerEdit(node);
19338             return false;
19339         }
19340         return true;
19341     },
19342
19343     // private
19344     updateNode : function(ed, value){
19345         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19346         this.editNode.setText(value);
19347     },
19348
19349     // private
19350     onHide : function(){
19351         Roo.tree.TreeEditor.superclass.onHide.call(this);
19352         if(this.editNode){
19353             this.editNode.ui.focus();
19354         }
19355     },
19356
19357     // private
19358     onSpecialKey : function(field, e){
19359         var k = e.getKey();
19360         if(k == e.ESC){
19361             e.stopEvent();
19362             this.cancelEdit();
19363         }else if(k == e.ENTER && !e.hasModifier()){
19364             e.stopEvent();
19365             this.completeEdit();
19366         }
19367     }
19368 });//<Script type="text/javascript">
19369 /*
19370  * Based on:
19371  * Ext JS Library 1.1.1
19372  * Copyright(c) 2006-2007, Ext JS, LLC.
19373  *
19374  * Originally Released Under LGPL - original licence link has changed is not relivant.
19375  *
19376  * Fork - LGPL
19377  * <script type="text/javascript">
19378  */
19379  
19380 /**
19381  * Not documented??? - probably should be...
19382  */
19383
19384 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19385     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19386     
19387     renderElements : function(n, a, targetNode, bulkRender){
19388         //consel.log("renderElements?");
19389         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19390
19391         var t = n.getOwnerTree();
19392         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19393         
19394         var cols = t.columns;
19395         var bw = t.borderWidth;
19396         var c = cols[0];
19397         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19398          var cb = typeof a.checked == "boolean";
19399         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19400         var colcls = 'x-t-' + tid + '-c0';
19401         var buf = [
19402             '<li class="x-tree-node">',
19403             
19404                 
19405                 '<div class="x-tree-node-el ', a.cls,'">',
19406                     // extran...
19407                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19408                 
19409                 
19410                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19411                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19412                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19413                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19414                            (a.iconCls ? ' '+a.iconCls : ''),
19415                            '" unselectable="on" />',
19416                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19417                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19418                              
19419                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19420                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19421                             '<span unselectable="on" qtip="' + tx + '">',
19422                              tx,
19423                              '</span></a>' ,
19424                     '</div>',
19425                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19426                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19427                  ];
19428         for(var i = 1, len = cols.length; i < len; i++){
19429             c = cols[i];
19430             colcls = 'x-t-' + tid + '-c' +i;
19431             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19432             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19433                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19434                       "</div>");
19435          }
19436          
19437          buf.push(
19438             '</a>',
19439             '<div class="x-clear"></div></div>',
19440             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19441             "</li>");
19442         
19443         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19444             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19445                                 n.nextSibling.ui.getEl(), buf.join(""));
19446         }else{
19447             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19448         }
19449         var el = this.wrap.firstChild;
19450         this.elRow = el;
19451         this.elNode = el.firstChild;
19452         this.ranchor = el.childNodes[1];
19453         this.ctNode = this.wrap.childNodes[1];
19454         var cs = el.firstChild.childNodes;
19455         this.indentNode = cs[0];
19456         this.ecNode = cs[1];
19457         this.iconNode = cs[2];
19458         var index = 3;
19459         if(cb){
19460             this.checkbox = cs[3];
19461             index++;
19462         }
19463         this.anchor = cs[index];
19464         
19465         this.textNode = cs[index].firstChild;
19466         
19467         //el.on("click", this.onClick, this);
19468         //el.on("dblclick", this.onDblClick, this);
19469         
19470         
19471        // console.log(this);
19472     },
19473     initEvents : function(){
19474         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19475         
19476             
19477         var a = this.ranchor;
19478
19479         var el = Roo.get(a);
19480
19481         if(Roo.isOpera){ // opera render bug ignores the CSS
19482             el.setStyle("text-decoration", "none");
19483         }
19484
19485         el.on("click", this.onClick, this);
19486         el.on("dblclick", this.onDblClick, this);
19487         el.on("contextmenu", this.onContextMenu, this);
19488         
19489     },
19490     
19491     /*onSelectedChange : function(state){
19492         if(state){
19493             this.focus();
19494             this.addClass("x-tree-selected");
19495         }else{
19496             //this.blur();
19497             this.removeClass("x-tree-selected");
19498         }
19499     },*/
19500     addClass : function(cls){
19501         if(this.elRow){
19502             Roo.fly(this.elRow).addClass(cls);
19503         }
19504         
19505     },
19506     
19507     
19508     removeClass : function(cls){
19509         if(this.elRow){
19510             Roo.fly(this.elRow).removeClass(cls);
19511         }
19512     }
19513
19514     
19515     
19516 });//<Script type="text/javascript">
19517
19518 /*
19519  * Based on:
19520  * Ext JS Library 1.1.1
19521  * Copyright(c) 2006-2007, Ext JS, LLC.
19522  *
19523  * Originally Released Under LGPL - original licence link has changed is not relivant.
19524  *
19525  * Fork - LGPL
19526  * <script type="text/javascript">
19527  */
19528  
19529
19530 /**
19531  * @class Roo.tree.ColumnTree
19532  * @extends Roo.data.TreePanel
19533  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19534  * @cfg {int} borderWidth  compined right/left border allowance
19535  * @constructor
19536  * @param {String/HTMLElement/Element} el The container element
19537  * @param {Object} config
19538  */
19539 Roo.tree.ColumnTree =  function(el, config)
19540 {
19541    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19542    this.addEvents({
19543         /**
19544         * @event resize
19545         * Fire this event on a container when it resizes
19546         * @param {int} w Width
19547         * @param {int} h Height
19548         */
19549        "resize" : true
19550     });
19551     this.on('resize', this.onResize, this);
19552 };
19553
19554 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19555     //lines:false,
19556     
19557     
19558     borderWidth: Roo.isBorderBox ? 0 : 2, 
19559     headEls : false,
19560     
19561     render : function(){
19562         // add the header.....
19563        
19564         Roo.tree.ColumnTree.superclass.render.apply(this);
19565         
19566         this.el.addClass('x-column-tree');
19567         
19568         this.headers = this.el.createChild(
19569             {cls:'x-tree-headers'},this.innerCt.dom);
19570    
19571         var cols = this.columns, c;
19572         var totalWidth = 0;
19573         this.headEls = [];
19574         var  len = cols.length;
19575         for(var i = 0; i < len; i++){
19576              c = cols[i];
19577              totalWidth += c.width;
19578             this.headEls.push(this.headers.createChild({
19579                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19580                  cn: {
19581                      cls:'x-tree-hd-text',
19582                      html: c.header
19583                  },
19584                  style:'width:'+(c.width-this.borderWidth)+'px;'
19585              }));
19586         }
19587         this.headers.createChild({cls:'x-clear'});
19588         // prevent floats from wrapping when clipped
19589         this.headers.setWidth(totalWidth);
19590         //this.innerCt.setWidth(totalWidth);
19591         this.innerCt.setStyle({ overflow: 'auto' });
19592         this.onResize(this.width, this.height);
19593              
19594         
19595     },
19596     onResize : function(w,h)
19597     {
19598         this.height = h;
19599         this.width = w;
19600         // resize cols..
19601         this.innerCt.setWidth(this.width);
19602         this.innerCt.setHeight(this.height-20);
19603         
19604         // headers...
19605         var cols = this.columns, c;
19606         var totalWidth = 0;
19607         var expEl = false;
19608         var len = cols.length;
19609         for(var i = 0; i < len; i++){
19610             c = cols[i];
19611             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19612                 // it's the expander..
19613                 expEl  = this.headEls[i];
19614                 continue;
19615             }
19616             totalWidth += c.width;
19617             
19618         }
19619         if (expEl) {
19620             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19621         }
19622         this.headers.setWidth(w-20);
19623
19624         
19625         
19626         
19627     }
19628 });
19629 /*
19630  * Based on:
19631  * Ext JS Library 1.1.1
19632  * Copyright(c) 2006-2007, Ext JS, LLC.
19633  *
19634  * Originally Released Under LGPL - original licence link has changed is not relivant.
19635  *
19636  * Fork - LGPL
19637  * <script type="text/javascript">
19638  */
19639  
19640 /**
19641  * @class Roo.menu.Menu
19642  * @extends Roo.util.Observable
19643  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19644  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19645  * @constructor
19646  * Creates a new Menu
19647  * @param {Object} config Configuration options
19648  */
19649 Roo.menu.Menu = function(config){
19650     Roo.apply(this, config);
19651     this.id = this.id || Roo.id();
19652     this.addEvents({
19653         /**
19654          * @event beforeshow
19655          * Fires before this menu is displayed
19656          * @param {Roo.menu.Menu} this
19657          */
19658         beforeshow : true,
19659         /**
19660          * @event beforehide
19661          * Fires before this menu is hidden
19662          * @param {Roo.menu.Menu} this
19663          */
19664         beforehide : true,
19665         /**
19666          * @event show
19667          * Fires after this menu is displayed
19668          * @param {Roo.menu.Menu} this
19669          */
19670         show : true,
19671         /**
19672          * @event hide
19673          * Fires after this menu is hidden
19674          * @param {Roo.menu.Menu} this
19675          */
19676         hide : true,
19677         /**
19678          * @event click
19679          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19680          * @param {Roo.menu.Menu} this
19681          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19682          * @param {Roo.EventObject} e
19683          */
19684         click : true,
19685         /**
19686          * @event mouseover
19687          * Fires when the mouse is hovering over this menu
19688          * @param {Roo.menu.Menu} this
19689          * @param {Roo.EventObject} e
19690          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19691          */
19692         mouseover : true,
19693         /**
19694          * @event mouseout
19695          * Fires when the mouse exits this menu
19696          * @param {Roo.menu.Menu} this
19697          * @param {Roo.EventObject} e
19698          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19699          */
19700         mouseout : true,
19701         /**
19702          * @event itemclick
19703          * Fires when a menu item contained in this menu is clicked
19704          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19705          * @param {Roo.EventObject} e
19706          */
19707         itemclick: true
19708     });
19709     if (this.registerMenu) {
19710         Roo.menu.MenuMgr.register(this);
19711     }
19712     
19713     var mis = this.items;
19714     this.items = new Roo.util.MixedCollection();
19715     if(mis){
19716         this.add.apply(this, mis);
19717     }
19718 };
19719
19720 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19721     /**
19722      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19723      */
19724     minWidth : 120,
19725     /**
19726      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19727      * for bottom-right shadow (defaults to "sides")
19728      */
19729     shadow : "sides",
19730     /**
19731      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19732      * this menu (defaults to "tl-tr?")
19733      */
19734     subMenuAlign : "tl-tr?",
19735     /**
19736      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19737      * relative to its element of origin (defaults to "tl-bl?")
19738      */
19739     defaultAlign : "tl-bl?",
19740     /**
19741      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19742      */
19743     allowOtherMenus : false,
19744     /**
19745      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19746      */
19747     registerMenu : true,
19748
19749     hidden:true,
19750
19751     // private
19752     render : function(){
19753         if(this.el){
19754             return;
19755         }
19756         var el = this.el = new Roo.Layer({
19757             cls: "x-menu",
19758             shadow:this.shadow,
19759             constrain: false,
19760             parentEl: this.parentEl || document.body,
19761             zindex:15000
19762         });
19763
19764         this.keyNav = new Roo.menu.MenuNav(this);
19765
19766         if(this.plain){
19767             el.addClass("x-menu-plain");
19768         }
19769         if(this.cls){
19770             el.addClass(this.cls);
19771         }
19772         // generic focus element
19773         this.focusEl = el.createChild({
19774             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19775         });
19776         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19777         ul.on("click", this.onClick, this);
19778         ul.on("mouseover", this.onMouseOver, this);
19779         ul.on("mouseout", this.onMouseOut, this);
19780         this.items.each(function(item){
19781             var li = document.createElement("li");
19782             li.className = "x-menu-list-item";
19783             ul.dom.appendChild(li);
19784             item.render(li, this);
19785         }, this);
19786         this.ul = ul;
19787         this.autoWidth();
19788     },
19789
19790     // private
19791     autoWidth : function(){
19792         var el = this.el, ul = this.ul;
19793         if(!el){
19794             return;
19795         }
19796         var w = this.width;
19797         if(w){
19798             el.setWidth(w);
19799         }else if(Roo.isIE){
19800             el.setWidth(this.minWidth);
19801             var t = el.dom.offsetWidth; // force recalc
19802             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19803         }
19804     },
19805
19806     // private
19807     delayAutoWidth : function(){
19808         if(this.rendered){
19809             if(!this.awTask){
19810                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19811             }
19812             this.awTask.delay(20);
19813         }
19814     },
19815
19816     // private
19817     findTargetItem : function(e){
19818         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19819         if(t && t.menuItemId){
19820             return this.items.get(t.menuItemId);
19821         }
19822     },
19823
19824     // private
19825     onClick : function(e){
19826         var t;
19827         if(t = this.findTargetItem(e)){
19828             t.onClick(e);
19829             this.fireEvent("click", this, t, e);
19830         }
19831     },
19832
19833     // private
19834     setActiveItem : function(item, autoExpand){
19835         if(item != this.activeItem){
19836             if(this.activeItem){
19837                 this.activeItem.deactivate();
19838             }
19839             this.activeItem = item;
19840             item.activate(autoExpand);
19841         }else if(autoExpand){
19842             item.expandMenu();
19843         }
19844     },
19845
19846     // private
19847     tryActivate : function(start, step){
19848         var items = this.items;
19849         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19850             var item = items.get(i);
19851             if(!item.disabled && item.canActivate){
19852                 this.setActiveItem(item, false);
19853                 return item;
19854             }
19855         }
19856         return false;
19857     },
19858
19859     // private
19860     onMouseOver : function(e){
19861         var t;
19862         if(t = this.findTargetItem(e)){
19863             if(t.canActivate && !t.disabled){
19864                 this.setActiveItem(t, true);
19865             }
19866         }
19867         this.fireEvent("mouseover", this, e, t);
19868     },
19869
19870     // private
19871     onMouseOut : function(e){
19872         var t;
19873         if(t = this.findTargetItem(e)){
19874             if(t == this.activeItem && t.shouldDeactivate(e)){
19875                 this.activeItem.deactivate();
19876                 delete this.activeItem;
19877             }
19878         }
19879         this.fireEvent("mouseout", this, e, t);
19880     },
19881
19882     /**
19883      * Read-only.  Returns true if the menu is currently displayed, else false.
19884      * @type Boolean
19885      */
19886     isVisible : function(){
19887         return this.el && !this.hidden;
19888     },
19889
19890     /**
19891      * Displays this menu relative to another element
19892      * @param {String/HTMLElement/Roo.Element} element The element to align to
19893      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19894      * the element (defaults to this.defaultAlign)
19895      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19896      */
19897     show : function(el, pos, parentMenu){
19898         this.parentMenu = parentMenu;
19899         if(!this.el){
19900             this.render();
19901         }
19902         this.fireEvent("beforeshow", this);
19903         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19904     },
19905
19906     /**
19907      * Displays this menu at a specific xy position
19908      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19909      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19910      */
19911     showAt : function(xy, parentMenu, /* private: */_e){
19912         this.parentMenu = parentMenu;
19913         if(!this.el){
19914             this.render();
19915         }
19916         if(_e !== false){
19917             this.fireEvent("beforeshow", this);
19918             xy = this.el.adjustForConstraints(xy);
19919         }
19920         this.el.setXY(xy);
19921         this.el.show();
19922         this.hidden = false;
19923         this.focus();
19924         this.fireEvent("show", this);
19925     },
19926
19927     focus : function(){
19928         if(!this.hidden){
19929             this.doFocus.defer(50, this);
19930         }
19931     },
19932
19933     doFocus : function(){
19934         if(!this.hidden){
19935             this.focusEl.focus();
19936         }
19937     },
19938
19939     /**
19940      * Hides this menu and optionally all parent menus
19941      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19942      */
19943     hide : function(deep){
19944         if(this.el && this.isVisible()){
19945             this.fireEvent("beforehide", this);
19946             if(this.activeItem){
19947                 this.activeItem.deactivate();
19948                 this.activeItem = null;
19949             }
19950             this.el.hide();
19951             this.hidden = true;
19952             this.fireEvent("hide", this);
19953         }
19954         if(deep === true && this.parentMenu){
19955             this.parentMenu.hide(true);
19956         }
19957     },
19958
19959     /**
19960      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19961      * Any of the following are valid:
19962      * <ul>
19963      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19964      * <li>An HTMLElement object which will be converted to a menu item</li>
19965      * <li>A menu item config object that will be created as a new menu item</li>
19966      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19967      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19968      * </ul>
19969      * Usage:
19970      * <pre><code>
19971 // Create the menu
19972 var menu = new Roo.menu.Menu();
19973
19974 // Create a menu item to add by reference
19975 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19976
19977 // Add a bunch of items at once using different methods.
19978 // Only the last item added will be returned.
19979 var item = menu.add(
19980     menuItem,                // add existing item by ref
19981     'Dynamic Item',          // new TextItem
19982     '-',                     // new separator
19983     { text: 'Config Item' }  // new item by config
19984 );
19985 </code></pre>
19986      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19987      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19988      */
19989     add : function(){
19990         var a = arguments, l = a.length, item;
19991         for(var i = 0; i < l; i++){
19992             var el = a[i];
19993             if ((typeof(el) == "object") && el.xtype && el.xns) {
19994                 el = Roo.factory(el, Roo.menu);
19995             }
19996             
19997             if(el.render){ // some kind of Item
19998                 item = this.addItem(el);
19999             }else if(typeof el == "string"){ // string
20000                 if(el == "separator" || el == "-"){
20001                     item = this.addSeparator();
20002                 }else{
20003                     item = this.addText(el);
20004                 }
20005             }else if(el.tagName || el.el){ // element
20006                 item = this.addElement(el);
20007             }else if(typeof el == "object"){ // must be menu item config?
20008                 item = this.addMenuItem(el);
20009             }
20010         }
20011         return item;
20012     },
20013
20014     /**
20015      * Returns this menu's underlying {@link Roo.Element} object
20016      * @return {Roo.Element} The element
20017      */
20018     getEl : function(){
20019         if(!this.el){
20020             this.render();
20021         }
20022         return this.el;
20023     },
20024
20025     /**
20026      * Adds a separator bar to the menu
20027      * @return {Roo.menu.Item} The menu item that was added
20028      */
20029     addSeparator : function(){
20030         return this.addItem(new Roo.menu.Separator());
20031     },
20032
20033     /**
20034      * Adds an {@link Roo.Element} object to the menu
20035      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20036      * @return {Roo.menu.Item} The menu item that was added
20037      */
20038     addElement : function(el){
20039         return this.addItem(new Roo.menu.BaseItem(el));
20040     },
20041
20042     /**
20043      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20044      * @param {Roo.menu.Item} item The menu item to add
20045      * @return {Roo.menu.Item} The menu item that was added
20046      */
20047     addItem : function(item){
20048         this.items.add(item);
20049         if(this.ul){
20050             var li = document.createElement("li");
20051             li.className = "x-menu-list-item";
20052             this.ul.dom.appendChild(li);
20053             item.render(li, this);
20054             this.delayAutoWidth();
20055         }
20056         return item;
20057     },
20058
20059     /**
20060      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20061      * @param {Object} config A MenuItem config object
20062      * @return {Roo.menu.Item} The menu item that was added
20063      */
20064     addMenuItem : function(config){
20065         if(!(config instanceof Roo.menu.Item)){
20066             if(typeof config.checked == "boolean"){ // must be check menu item config?
20067                 config = new Roo.menu.CheckItem(config);
20068             }else{
20069                 config = new Roo.menu.Item(config);
20070             }
20071         }
20072         return this.addItem(config);
20073     },
20074
20075     /**
20076      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20077      * @param {String} text The text to display in the menu item
20078      * @return {Roo.menu.Item} The menu item that was added
20079      */
20080     addText : function(text){
20081         return this.addItem(new Roo.menu.TextItem({ text : text }));
20082     },
20083
20084     /**
20085      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20086      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20087      * @param {Roo.menu.Item} item The menu item to add
20088      * @return {Roo.menu.Item} The menu item that was added
20089      */
20090     insert : function(index, item){
20091         this.items.insert(index, item);
20092         if(this.ul){
20093             var li = document.createElement("li");
20094             li.className = "x-menu-list-item";
20095             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20096             item.render(li, this);
20097             this.delayAutoWidth();
20098         }
20099         return item;
20100     },
20101
20102     /**
20103      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20104      * @param {Roo.menu.Item} item The menu item to remove
20105      */
20106     remove : function(item){
20107         this.items.removeKey(item.id);
20108         item.destroy();
20109     },
20110
20111     /**
20112      * Removes and destroys all items in the menu
20113      */
20114     removeAll : function(){
20115         var f;
20116         while(f = this.items.first()){
20117             this.remove(f);
20118         }
20119     }
20120 });
20121
20122 // MenuNav is a private utility class used internally by the Menu
20123 Roo.menu.MenuNav = function(menu){
20124     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20125     this.scope = this.menu = menu;
20126 };
20127
20128 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20129     doRelay : function(e, h){
20130         var k = e.getKey();
20131         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20132             this.menu.tryActivate(0, 1);
20133             return false;
20134         }
20135         return h.call(this.scope || this, e, this.menu);
20136     },
20137
20138     up : function(e, m){
20139         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20140             m.tryActivate(m.items.length-1, -1);
20141         }
20142     },
20143
20144     down : function(e, m){
20145         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20146             m.tryActivate(0, 1);
20147         }
20148     },
20149
20150     right : function(e, m){
20151         if(m.activeItem){
20152             m.activeItem.expandMenu(true);
20153         }
20154     },
20155
20156     left : function(e, m){
20157         m.hide();
20158         if(m.parentMenu && m.parentMenu.activeItem){
20159             m.parentMenu.activeItem.activate();
20160         }
20161     },
20162
20163     enter : function(e, m){
20164         if(m.activeItem){
20165             e.stopPropagation();
20166             m.activeItem.onClick(e);
20167             m.fireEvent("click", this, m.activeItem);
20168             return true;
20169         }
20170     }
20171 });/*
20172  * Based on:
20173  * Ext JS Library 1.1.1
20174  * Copyright(c) 2006-2007, Ext JS, LLC.
20175  *
20176  * Originally Released Under LGPL - original licence link has changed is not relivant.
20177  *
20178  * Fork - LGPL
20179  * <script type="text/javascript">
20180  */
20181  
20182 /**
20183  * @class Roo.menu.MenuMgr
20184  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20185  * @singleton
20186  */
20187 Roo.menu.MenuMgr = function(){
20188    var menus, active, groups = {}, attached = false, lastShow = new Date();
20189
20190    // private - called when first menu is created
20191    function init(){
20192        menus = {};
20193        active = new Roo.util.MixedCollection();
20194        Roo.get(document).addKeyListener(27, function(){
20195            if(active.length > 0){
20196                hideAll();
20197            }
20198        });
20199    }
20200
20201    // private
20202    function hideAll(){
20203        if(active && active.length > 0){
20204            var c = active.clone();
20205            c.each(function(m){
20206                m.hide();
20207            });
20208        }
20209    }
20210
20211    // private
20212    function onHide(m){
20213        active.remove(m);
20214        if(active.length < 1){
20215            Roo.get(document).un("mousedown", onMouseDown);
20216            attached = false;
20217        }
20218    }
20219
20220    // private
20221    function onShow(m){
20222        var last = active.last();
20223        lastShow = new Date();
20224        active.add(m);
20225        if(!attached){
20226            Roo.get(document).on("mousedown", onMouseDown);
20227            attached = true;
20228        }
20229        if(m.parentMenu){
20230           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20231           m.parentMenu.activeChild = m;
20232        }else if(last && last.isVisible()){
20233           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20234        }
20235    }
20236
20237    // private
20238    function onBeforeHide(m){
20239        if(m.activeChild){
20240            m.activeChild.hide();
20241        }
20242        if(m.autoHideTimer){
20243            clearTimeout(m.autoHideTimer);
20244            delete m.autoHideTimer;
20245        }
20246    }
20247
20248    // private
20249    function onBeforeShow(m){
20250        var pm = m.parentMenu;
20251        if(!pm && !m.allowOtherMenus){
20252            hideAll();
20253        }else if(pm && pm.activeChild && active != m){
20254            pm.activeChild.hide();
20255        }
20256    }
20257
20258    // private
20259    function onMouseDown(e){
20260        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20261            hideAll();
20262        }
20263    }
20264
20265    // private
20266    function onBeforeCheck(mi, state){
20267        if(state){
20268            var g = groups[mi.group];
20269            for(var i = 0, l = g.length; i < l; i++){
20270                if(g[i] != mi){
20271                    g[i].setChecked(false);
20272                }
20273            }
20274        }
20275    }
20276
20277    return {
20278
20279        /**
20280         * Hides all menus that are currently visible
20281         */
20282        hideAll : function(){
20283             hideAll();  
20284        },
20285
20286        // private
20287        register : function(menu){
20288            if(!menus){
20289                init();
20290            }
20291            menus[menu.id] = menu;
20292            menu.on("beforehide", onBeforeHide);
20293            menu.on("hide", onHide);
20294            menu.on("beforeshow", onBeforeShow);
20295            menu.on("show", onShow);
20296            var g = menu.group;
20297            if(g && menu.events["checkchange"]){
20298                if(!groups[g]){
20299                    groups[g] = [];
20300                }
20301                groups[g].push(menu);
20302                menu.on("checkchange", onCheck);
20303            }
20304        },
20305
20306         /**
20307          * Returns a {@link Roo.menu.Menu} object
20308          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20309          * be used to generate and return a new Menu instance.
20310          */
20311        get : function(menu){
20312            if(typeof menu == "string"){ // menu id
20313                return menus[menu];
20314            }else if(menu.events){  // menu instance
20315                return menu;
20316            }else if(typeof menu.length == 'number'){ // array of menu items?
20317                return new Roo.menu.Menu({items:menu});
20318            }else{ // otherwise, must be a config
20319                return new Roo.menu.Menu(menu);
20320            }
20321        },
20322
20323        // private
20324        unregister : function(menu){
20325            delete menus[menu.id];
20326            menu.un("beforehide", onBeforeHide);
20327            menu.un("hide", onHide);
20328            menu.un("beforeshow", onBeforeShow);
20329            menu.un("show", onShow);
20330            var g = menu.group;
20331            if(g && menu.events["checkchange"]){
20332                groups[g].remove(menu);
20333                menu.un("checkchange", onCheck);
20334            }
20335        },
20336
20337        // private
20338        registerCheckable : function(menuItem){
20339            var g = menuItem.group;
20340            if(g){
20341                if(!groups[g]){
20342                    groups[g] = [];
20343                }
20344                groups[g].push(menuItem);
20345                menuItem.on("beforecheckchange", onBeforeCheck);
20346            }
20347        },
20348
20349        // private
20350        unregisterCheckable : function(menuItem){
20351            var g = menuItem.group;
20352            if(g){
20353                groups[g].remove(menuItem);
20354                menuItem.un("beforecheckchange", onBeforeCheck);
20355            }
20356        }
20357    };
20358 }();/*
20359  * Based on:
20360  * Ext JS Library 1.1.1
20361  * Copyright(c) 2006-2007, Ext JS, LLC.
20362  *
20363  * Originally Released Under LGPL - original licence link has changed is not relivant.
20364  *
20365  * Fork - LGPL
20366  * <script type="text/javascript">
20367  */
20368  
20369
20370 /**
20371  * @class Roo.menu.BaseItem
20372  * @extends Roo.Component
20373  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20374  * management and base configuration options shared by all menu components.
20375  * @constructor
20376  * Creates a new BaseItem
20377  * @param {Object} config Configuration options
20378  */
20379 Roo.menu.BaseItem = function(config){
20380     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20381
20382     this.addEvents({
20383         /**
20384          * @event click
20385          * Fires when this item is clicked
20386          * @param {Roo.menu.BaseItem} this
20387          * @param {Roo.EventObject} e
20388          */
20389         click: true,
20390         /**
20391          * @event activate
20392          * Fires when this item is activated
20393          * @param {Roo.menu.BaseItem} this
20394          */
20395         activate : true,
20396         /**
20397          * @event deactivate
20398          * Fires when this item is deactivated
20399          * @param {Roo.menu.BaseItem} this
20400          */
20401         deactivate : true
20402     });
20403
20404     if(this.handler){
20405         this.on("click", this.handler, this.scope, true);
20406     }
20407 };
20408
20409 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20410     /**
20411      * @cfg {Function} handler
20412      * A function that will handle the click event of this menu item (defaults to undefined)
20413      */
20414     /**
20415      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20416      */
20417     canActivate : false,
20418     /**
20419      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20420      */
20421     activeClass : "x-menu-item-active",
20422     /**
20423      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20424      */
20425     hideOnClick : true,
20426     /**
20427      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20428      */
20429     hideDelay : 100,
20430
20431     // private
20432     ctype: "Roo.menu.BaseItem",
20433
20434     // private
20435     actionMode : "container",
20436
20437     // private
20438     render : function(container, parentMenu){
20439         this.parentMenu = parentMenu;
20440         Roo.menu.BaseItem.superclass.render.call(this, container);
20441         this.container.menuItemId = this.id;
20442     },
20443
20444     // private
20445     onRender : function(container, position){
20446         this.el = Roo.get(this.el);
20447         container.dom.appendChild(this.el.dom);
20448     },
20449
20450     // private
20451     onClick : function(e){
20452         if(!this.disabled && this.fireEvent("click", this, e) !== false
20453                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20454             this.handleClick(e);
20455         }else{
20456             e.stopEvent();
20457         }
20458     },
20459
20460     // private
20461     activate : function(){
20462         if(this.disabled){
20463             return false;
20464         }
20465         var li = this.container;
20466         li.addClass(this.activeClass);
20467         this.region = li.getRegion().adjust(2, 2, -2, -2);
20468         this.fireEvent("activate", this);
20469         return true;
20470     },
20471
20472     // private
20473     deactivate : function(){
20474         this.container.removeClass(this.activeClass);
20475         this.fireEvent("deactivate", this);
20476     },
20477
20478     // private
20479     shouldDeactivate : function(e){
20480         return !this.region || !this.region.contains(e.getPoint());
20481     },
20482
20483     // private
20484     handleClick : function(e){
20485         if(this.hideOnClick){
20486             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20487         }
20488     },
20489
20490     // private
20491     expandMenu : function(autoActivate){
20492         // do nothing
20493     },
20494
20495     // private
20496     hideMenu : function(){
20497         // do nothing
20498     }
20499 });/*
20500  * Based on:
20501  * Ext JS Library 1.1.1
20502  * Copyright(c) 2006-2007, Ext JS, LLC.
20503  *
20504  * Originally Released Under LGPL - original licence link has changed is not relivant.
20505  *
20506  * Fork - LGPL
20507  * <script type="text/javascript">
20508  */
20509  
20510 /**
20511  * @class Roo.menu.Adapter
20512  * @extends Roo.menu.BaseItem
20513  * 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.
20514  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20515  * @constructor
20516  * Creates a new Adapter
20517  * @param {Object} config Configuration options
20518  */
20519 Roo.menu.Adapter = function(component, config){
20520     Roo.menu.Adapter.superclass.constructor.call(this, config);
20521     this.component = component;
20522 };
20523 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20524     // private
20525     canActivate : true,
20526
20527     // private
20528     onRender : function(container, position){
20529         this.component.render(container);
20530         this.el = this.component.getEl();
20531     },
20532
20533     // private
20534     activate : function(){
20535         if(this.disabled){
20536             return false;
20537         }
20538         this.component.focus();
20539         this.fireEvent("activate", this);
20540         return true;
20541     },
20542
20543     // private
20544     deactivate : function(){
20545         this.fireEvent("deactivate", this);
20546     },
20547
20548     // private
20549     disable : function(){
20550         this.component.disable();
20551         Roo.menu.Adapter.superclass.disable.call(this);
20552     },
20553
20554     // private
20555     enable : function(){
20556         this.component.enable();
20557         Roo.menu.Adapter.superclass.enable.call(this);
20558     }
20559 });/*
20560  * Based on:
20561  * Ext JS Library 1.1.1
20562  * Copyright(c) 2006-2007, Ext JS, LLC.
20563  *
20564  * Originally Released Under LGPL - original licence link has changed is not relivant.
20565  *
20566  * Fork - LGPL
20567  * <script type="text/javascript">
20568  */
20569
20570 /**
20571  * @class Roo.menu.TextItem
20572  * @extends Roo.menu.BaseItem
20573  * Adds a static text string to a menu, usually used as either a heading or group separator.
20574  * Note: old style constructor with text is still supported.
20575  * 
20576  * @constructor
20577  * Creates a new TextItem
20578  * @param {Object} cfg Configuration
20579  */
20580 Roo.menu.TextItem = function(cfg){
20581     if (typeof(cfg) == 'string') {
20582         this.text = cfg;
20583     } else {
20584         Roo.apply(this,cfg);
20585     }
20586     
20587     Roo.menu.TextItem.superclass.constructor.call(this);
20588 };
20589
20590 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20591     /**
20592      * @cfg {Boolean} text Text to show on item.
20593      */
20594     text : '',
20595     
20596     /**
20597      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20598      */
20599     hideOnClick : false,
20600     /**
20601      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20602      */
20603     itemCls : "x-menu-text",
20604
20605     // private
20606     onRender : function(){
20607         var s = document.createElement("span");
20608         s.className = this.itemCls;
20609         s.innerHTML = this.text;
20610         this.el = s;
20611         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20612     }
20613 });/*
20614  * Based on:
20615  * Ext JS Library 1.1.1
20616  * Copyright(c) 2006-2007, Ext JS, LLC.
20617  *
20618  * Originally Released Under LGPL - original licence link has changed is not relivant.
20619  *
20620  * Fork - LGPL
20621  * <script type="text/javascript">
20622  */
20623
20624 /**
20625  * @class Roo.menu.Separator
20626  * @extends Roo.menu.BaseItem
20627  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20628  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20629  * @constructor
20630  * @param {Object} config Configuration options
20631  */
20632 Roo.menu.Separator = function(config){
20633     Roo.menu.Separator.superclass.constructor.call(this, config);
20634 };
20635
20636 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20637     /**
20638      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20639      */
20640     itemCls : "x-menu-sep",
20641     /**
20642      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20643      */
20644     hideOnClick : false,
20645
20646     // private
20647     onRender : function(li){
20648         var s = document.createElement("span");
20649         s.className = this.itemCls;
20650         s.innerHTML = "&#160;";
20651         this.el = s;
20652         li.addClass("x-menu-sep-li");
20653         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20654     }
20655 });/*
20656  * Based on:
20657  * Ext JS Library 1.1.1
20658  * Copyright(c) 2006-2007, Ext JS, LLC.
20659  *
20660  * Originally Released Under LGPL - original licence link has changed is not relivant.
20661  *
20662  * Fork - LGPL
20663  * <script type="text/javascript">
20664  */
20665 /**
20666  * @class Roo.menu.Item
20667  * @extends Roo.menu.BaseItem
20668  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20669  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20670  * activation and click handling.
20671  * @constructor
20672  * Creates a new Item
20673  * @param {Object} config Configuration options
20674  */
20675 Roo.menu.Item = function(config){
20676     Roo.menu.Item.superclass.constructor.call(this, config);
20677     if(this.menu){
20678         this.menu = Roo.menu.MenuMgr.get(this.menu);
20679     }
20680 };
20681 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20682     
20683     /**
20684      * @cfg {String} text
20685      * The text to show on the menu item.
20686      */
20687     text: '',
20688      /**
20689      * @cfg {String} HTML to render in menu
20690      * The text to show on the menu item (HTML version).
20691      */
20692     html: '',
20693     /**
20694      * @cfg {String} icon
20695      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20696      */
20697     icon: undefined,
20698     /**
20699      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20700      */
20701     itemCls : "x-menu-item",
20702     /**
20703      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20704      */
20705     canActivate : true,
20706     /**
20707      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20708      */
20709     showDelay: 200,
20710     // doc'd in BaseItem
20711     hideDelay: 200,
20712
20713     // private
20714     ctype: "Roo.menu.Item",
20715     
20716     // private
20717     onRender : function(container, position){
20718         var el = document.createElement("a");
20719         el.hideFocus = true;
20720         el.unselectable = "on";
20721         el.href = this.href || "#";
20722         if(this.hrefTarget){
20723             el.target = this.hrefTarget;
20724         }
20725         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20726         
20727         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20728         
20729         el.innerHTML = String.format(
20730                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20731                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20732         this.el = el;
20733         Roo.menu.Item.superclass.onRender.call(this, container, position);
20734     },
20735
20736     /**
20737      * Sets the text to display in this menu item
20738      * @param {String} text The text to display
20739      * @param {Boolean} isHTML true to indicate text is pure html.
20740      */
20741     setText : function(text, isHTML){
20742         if (isHTML) {
20743             this.html = text;
20744         } else {
20745             this.text = text;
20746             this.html = '';
20747         }
20748         if(this.rendered){
20749             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20750      
20751             this.el.update(String.format(
20752                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20753                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20754             this.parentMenu.autoWidth();
20755         }
20756     },
20757
20758     // private
20759     handleClick : function(e){
20760         if(!this.href){ // if no link defined, stop the event automatically
20761             e.stopEvent();
20762         }
20763         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20764     },
20765
20766     // private
20767     activate : function(autoExpand){
20768         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20769             this.focus();
20770             if(autoExpand){
20771                 this.expandMenu();
20772             }
20773         }
20774         return true;
20775     },
20776
20777     // private
20778     shouldDeactivate : function(e){
20779         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20780             if(this.menu && this.menu.isVisible()){
20781                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20782             }
20783             return true;
20784         }
20785         return false;
20786     },
20787
20788     // private
20789     deactivate : function(){
20790         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20791         this.hideMenu();
20792     },
20793
20794     // private
20795     expandMenu : function(autoActivate){
20796         if(!this.disabled && this.menu){
20797             clearTimeout(this.hideTimer);
20798             delete this.hideTimer;
20799             if(!this.menu.isVisible() && !this.showTimer){
20800                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20801             }else if (this.menu.isVisible() && autoActivate){
20802                 this.menu.tryActivate(0, 1);
20803             }
20804         }
20805     },
20806
20807     // private
20808     deferExpand : function(autoActivate){
20809         delete this.showTimer;
20810         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20811         if(autoActivate){
20812             this.menu.tryActivate(0, 1);
20813         }
20814     },
20815
20816     // private
20817     hideMenu : function(){
20818         clearTimeout(this.showTimer);
20819         delete this.showTimer;
20820         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20821             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20822         }
20823     },
20824
20825     // private
20826     deferHide : function(){
20827         delete this.hideTimer;
20828         this.menu.hide();
20829     }
20830 });/*
20831  * Based on:
20832  * Ext JS Library 1.1.1
20833  * Copyright(c) 2006-2007, Ext JS, LLC.
20834  *
20835  * Originally Released Under LGPL - original licence link has changed is not relivant.
20836  *
20837  * Fork - LGPL
20838  * <script type="text/javascript">
20839  */
20840  
20841 /**
20842  * @class Roo.menu.CheckItem
20843  * @extends Roo.menu.Item
20844  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20845  * @constructor
20846  * Creates a new CheckItem
20847  * @param {Object} config Configuration options
20848  */
20849 Roo.menu.CheckItem = function(config){
20850     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20851     this.addEvents({
20852         /**
20853          * @event beforecheckchange
20854          * Fires before the checked value is set, providing an opportunity to cancel if needed
20855          * @param {Roo.menu.CheckItem} this
20856          * @param {Boolean} checked The new checked value that will be set
20857          */
20858         "beforecheckchange" : true,
20859         /**
20860          * @event checkchange
20861          * Fires after the checked value has been set
20862          * @param {Roo.menu.CheckItem} this
20863          * @param {Boolean} checked The checked value that was set
20864          */
20865         "checkchange" : true
20866     });
20867     if(this.checkHandler){
20868         this.on('checkchange', this.checkHandler, this.scope);
20869     }
20870 };
20871 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20872     /**
20873      * @cfg {String} group
20874      * All check items with the same group name will automatically be grouped into a single-select
20875      * radio button group (defaults to '')
20876      */
20877     /**
20878      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20879      */
20880     itemCls : "x-menu-item x-menu-check-item",
20881     /**
20882      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20883      */
20884     groupClass : "x-menu-group-item",
20885
20886     /**
20887      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20888      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20889      * initialized with checked = true will be rendered as checked.
20890      */
20891     checked: false,
20892
20893     // private
20894     ctype: "Roo.menu.CheckItem",
20895
20896     // private
20897     onRender : function(c){
20898         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20899         if(this.group){
20900             this.el.addClass(this.groupClass);
20901         }
20902         Roo.menu.MenuMgr.registerCheckable(this);
20903         if(this.checked){
20904             this.checked = false;
20905             this.setChecked(true, true);
20906         }
20907     },
20908
20909     // private
20910     destroy : function(){
20911         if(this.rendered){
20912             Roo.menu.MenuMgr.unregisterCheckable(this);
20913         }
20914         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20915     },
20916
20917     /**
20918      * Set the checked state of this item
20919      * @param {Boolean} checked The new checked value
20920      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20921      */
20922     setChecked : function(state, suppressEvent){
20923         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20924             if(this.container){
20925                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20926             }
20927             this.checked = state;
20928             if(suppressEvent !== true){
20929                 this.fireEvent("checkchange", this, state);
20930             }
20931         }
20932     },
20933
20934     // private
20935     handleClick : function(e){
20936        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20937            this.setChecked(!this.checked);
20938        }
20939        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20940     }
20941 });/*
20942  * Based on:
20943  * Ext JS Library 1.1.1
20944  * Copyright(c) 2006-2007, Ext JS, LLC.
20945  *
20946  * Originally Released Under LGPL - original licence link has changed is not relivant.
20947  *
20948  * Fork - LGPL
20949  * <script type="text/javascript">
20950  */
20951  
20952 /**
20953  * @class Roo.menu.DateItem
20954  * @extends Roo.menu.Adapter
20955  * A menu item that wraps the {@link Roo.DatPicker} component.
20956  * @constructor
20957  * Creates a new DateItem
20958  * @param {Object} config Configuration options
20959  */
20960 Roo.menu.DateItem = function(config){
20961     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20962     /** The Roo.DatePicker object @type Roo.DatePicker */
20963     this.picker = this.component;
20964     this.addEvents({select: true});
20965     
20966     this.picker.on("render", function(picker){
20967         picker.getEl().swallowEvent("click");
20968         picker.container.addClass("x-menu-date-item");
20969     });
20970
20971     this.picker.on("select", this.onSelect, this);
20972 };
20973
20974 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20975     // private
20976     onSelect : function(picker, date){
20977         this.fireEvent("select", this, date, picker);
20978         Roo.menu.DateItem.superclass.handleClick.call(this);
20979     }
20980 });/*
20981  * Based on:
20982  * Ext JS Library 1.1.1
20983  * Copyright(c) 2006-2007, Ext JS, LLC.
20984  *
20985  * Originally Released Under LGPL - original licence link has changed is not relivant.
20986  *
20987  * Fork - LGPL
20988  * <script type="text/javascript">
20989  */
20990  
20991 /**
20992  * @class Roo.menu.ColorItem
20993  * @extends Roo.menu.Adapter
20994  * A menu item that wraps the {@link Roo.ColorPalette} component.
20995  * @constructor
20996  * Creates a new ColorItem
20997  * @param {Object} config Configuration options
20998  */
20999 Roo.menu.ColorItem = function(config){
21000     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21001     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21002     this.palette = this.component;
21003     this.relayEvents(this.palette, ["select"]);
21004     if(this.selectHandler){
21005         this.on('select', this.selectHandler, this.scope);
21006     }
21007 };
21008 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21009  * Based on:
21010  * Ext JS Library 1.1.1
21011  * Copyright(c) 2006-2007, Ext JS, LLC.
21012  *
21013  * Originally Released Under LGPL - original licence link has changed is not relivant.
21014  *
21015  * Fork - LGPL
21016  * <script type="text/javascript">
21017  */
21018  
21019
21020 /**
21021  * @class Roo.menu.DateMenu
21022  * @extends Roo.menu.Menu
21023  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21024  * @constructor
21025  * Creates a new DateMenu
21026  * @param {Object} config Configuration options
21027  */
21028 Roo.menu.DateMenu = function(config){
21029     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21030     this.plain = true;
21031     var di = new Roo.menu.DateItem(config);
21032     this.add(di);
21033     /**
21034      * The {@link Roo.DatePicker} instance for this DateMenu
21035      * @type DatePicker
21036      */
21037     this.picker = di.picker;
21038     /**
21039      * @event select
21040      * @param {DatePicker} picker
21041      * @param {Date} date
21042      */
21043     this.relayEvents(di, ["select"]);
21044
21045     this.on('beforeshow', function(){
21046         if(this.picker){
21047             this.picker.hideMonthPicker(true);
21048         }
21049     }, this);
21050 };
21051 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21052     cls:'x-date-menu'
21053 });/*
21054  * Based on:
21055  * Ext JS Library 1.1.1
21056  * Copyright(c) 2006-2007, Ext JS, LLC.
21057  *
21058  * Originally Released Under LGPL - original licence link has changed is not relivant.
21059  *
21060  * Fork - LGPL
21061  * <script type="text/javascript">
21062  */
21063  
21064
21065 /**
21066  * @class Roo.menu.ColorMenu
21067  * @extends Roo.menu.Menu
21068  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21069  * @constructor
21070  * Creates a new ColorMenu
21071  * @param {Object} config Configuration options
21072  */
21073 Roo.menu.ColorMenu = function(config){
21074     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21075     this.plain = true;
21076     var ci = new Roo.menu.ColorItem(config);
21077     this.add(ci);
21078     /**
21079      * The {@link Roo.ColorPalette} instance for this ColorMenu
21080      * @type ColorPalette
21081      */
21082     this.palette = ci.palette;
21083     /**
21084      * @event select
21085      * @param {ColorPalette} palette
21086      * @param {String} color
21087      */
21088     this.relayEvents(ci, ["select"]);
21089 };
21090 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21091  * Based on:
21092  * Ext JS Library 1.1.1
21093  * Copyright(c) 2006-2007, Ext JS, LLC.
21094  *
21095  * Originally Released Under LGPL - original licence link has changed is not relivant.
21096  *
21097  * Fork - LGPL
21098  * <script type="text/javascript">
21099  */
21100  
21101 /**
21102  * @class Roo.form.Field
21103  * @extends Roo.BoxComponent
21104  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21105  * @constructor
21106  * Creates a new Field
21107  * @param {Object} config Configuration options
21108  */
21109 Roo.form.Field = function(config){
21110     Roo.form.Field.superclass.constructor.call(this, config);
21111 };
21112
21113 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21114     /**
21115      * @cfg {String} fieldLabel Label to use when rendering a form.
21116      */
21117        /**
21118      * @cfg {String} qtip Mouse over tip
21119      */
21120      
21121     /**
21122      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21123      */
21124     invalidClass : "x-form-invalid",
21125     /**
21126      * @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")
21127      */
21128     invalidText : "The value in this field is invalid",
21129     /**
21130      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21131      */
21132     focusClass : "x-form-focus",
21133     /**
21134      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21135       automatic validation (defaults to "keyup").
21136      */
21137     validationEvent : "keyup",
21138     /**
21139      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21140      */
21141     validateOnBlur : true,
21142     /**
21143      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21144      */
21145     validationDelay : 250,
21146     /**
21147      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21148      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21149      */
21150     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21151     /**
21152      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21153      */
21154     fieldClass : "x-form-field",
21155     /**
21156      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21157      *<pre>
21158 Value         Description
21159 -----------   ----------------------------------------------------------------------
21160 qtip          Display a quick tip when the user hovers over the field
21161 title         Display a default browser title attribute popup
21162 under         Add a block div beneath the field containing the error text
21163 side          Add an error icon to the right of the field with a popup on hover
21164 [element id]  Add the error text directly to the innerHTML of the specified element
21165 </pre>
21166      */
21167     msgTarget : 'qtip',
21168     /**
21169      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21170      */
21171     msgFx : 'normal',
21172
21173     /**
21174      * @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.
21175      */
21176     readOnly : false,
21177
21178     /**
21179      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21180      */
21181     disabled : false,
21182
21183     /**
21184      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21185      */
21186     inputType : undefined,
21187     
21188     /**
21189      * @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).
21190          */
21191         tabIndex : undefined,
21192         
21193     // private
21194     isFormField : true,
21195
21196     // private
21197     hasFocus : false,
21198     /**
21199      * @property {Roo.Element} fieldEl
21200      * Element Containing the rendered Field (with label etc.)
21201      */
21202     /**
21203      * @cfg {Mixed} value A value to initialize this field with.
21204      */
21205     value : undefined,
21206
21207     /**
21208      * @cfg {String} name The field's HTML name attribute.
21209      */
21210     /**
21211      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21212      */
21213
21214         // private ??
21215         initComponent : function(){
21216         Roo.form.Field.superclass.initComponent.call(this);
21217         this.addEvents({
21218             /**
21219              * @event focus
21220              * Fires when this field receives input focus.
21221              * @param {Roo.form.Field} this
21222              */
21223             focus : true,
21224             /**
21225              * @event blur
21226              * Fires when this field loses input focus.
21227              * @param {Roo.form.Field} this
21228              */
21229             blur : true,
21230             /**
21231              * @event specialkey
21232              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21233              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21234              * @param {Roo.form.Field} this
21235              * @param {Roo.EventObject} e The event object
21236              */
21237             specialkey : true,
21238             /**
21239              * @event change
21240              * Fires just before the field blurs if the field value has changed.
21241              * @param {Roo.form.Field} this
21242              * @param {Mixed} newValue The new value
21243              * @param {Mixed} oldValue The original value
21244              */
21245             change : true,
21246             /**
21247              * @event invalid
21248              * Fires after the field has been marked as invalid.
21249              * @param {Roo.form.Field} this
21250              * @param {String} msg The validation message
21251              */
21252             invalid : true,
21253             /**
21254              * @event valid
21255              * Fires after the field has been validated with no errors.
21256              * @param {Roo.form.Field} this
21257              */
21258             valid : true,
21259              /**
21260              * @event keyup
21261              * Fires after the key up
21262              * @param {Roo.form.Field} this
21263              * @param {Roo.EventObject}  e The event Object
21264              */
21265             keyup : true
21266         });
21267     },
21268
21269     /**
21270      * Returns the name attribute of the field if available
21271      * @return {String} name The field name
21272      */
21273     getName: function(){
21274          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21275     },
21276
21277     // private
21278     onRender : function(ct, position){
21279         Roo.form.Field.superclass.onRender.call(this, ct, position);
21280         if(!this.el){
21281             var cfg = this.getAutoCreate();
21282             if(!cfg.name){
21283                 cfg.name = this.name || this.id;
21284             }
21285             if(this.inputType){
21286                 cfg.type = this.inputType;
21287             }
21288             this.el = ct.createChild(cfg, position);
21289         }
21290         var type = this.el.dom.type;
21291         if(type){
21292             if(type == 'password'){
21293                 type = 'text';
21294             }
21295             this.el.addClass('x-form-'+type);
21296         }
21297         if(this.readOnly){
21298             this.el.dom.readOnly = true;
21299         }
21300         if(this.tabIndex !== undefined){
21301             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21302         }
21303
21304         this.el.addClass([this.fieldClass, this.cls]);
21305         this.initValue();
21306     },
21307
21308     /**
21309      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21310      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21311      * @return {Roo.form.Field} this
21312      */
21313     applyTo : function(target){
21314         this.allowDomMove = false;
21315         this.el = Roo.get(target);
21316         this.render(this.el.dom.parentNode);
21317         return this;
21318     },
21319
21320     // private
21321     initValue : function(){
21322         if(this.value !== undefined){
21323             this.setValue(this.value);
21324         }else if(this.el.dom.value.length > 0){
21325             this.setValue(this.el.dom.value);
21326         }
21327     },
21328
21329     /**
21330      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21331      */
21332     isDirty : function() {
21333         if(this.disabled) {
21334             return false;
21335         }
21336         return String(this.getValue()) !== String(this.originalValue);
21337     },
21338
21339     // private
21340     afterRender : function(){
21341         Roo.form.Field.superclass.afterRender.call(this);
21342         this.initEvents();
21343     },
21344
21345     // private
21346     fireKey : function(e){
21347         //Roo.log('field ' + e.getKey());
21348         if(e.isNavKeyPress()){
21349             this.fireEvent("specialkey", this, e);
21350         }
21351     },
21352
21353     /**
21354      * Resets the current field value to the originally loaded value and clears any validation messages
21355      */
21356     reset : function(){
21357         this.setValue(this.originalValue);
21358         this.clearInvalid();
21359     },
21360
21361     // private
21362     initEvents : function(){
21363         // safari killled keypress - so keydown is now used..
21364         this.el.on("keydown" , this.fireKey,  this);
21365         this.el.on("focus", this.onFocus,  this);
21366         this.el.on("blur", this.onBlur,  this);
21367         this.el.relayEvent('keyup', this);
21368
21369         // reference to original value for reset
21370         this.originalValue = this.getValue();
21371     },
21372
21373     // private
21374     onFocus : function(){
21375         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21376             this.el.addClass(this.focusClass);
21377         }
21378         if(!this.hasFocus){
21379             this.hasFocus = true;
21380             this.startValue = this.getValue();
21381             this.fireEvent("focus", this);
21382         }
21383     },
21384
21385     beforeBlur : Roo.emptyFn,
21386
21387     // private
21388     onBlur : function(){
21389         this.beforeBlur();
21390         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21391             this.el.removeClass(this.focusClass);
21392         }
21393         this.hasFocus = false;
21394         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21395             this.validate();
21396         }
21397         var v = this.getValue();
21398         if(String(v) !== String(this.startValue)){
21399             this.fireEvent('change', this, v, this.startValue);
21400         }
21401         this.fireEvent("blur", this);
21402     },
21403
21404     /**
21405      * Returns whether or not the field value is currently valid
21406      * @param {Boolean} preventMark True to disable marking the field invalid
21407      * @return {Boolean} True if the value is valid, else false
21408      */
21409     isValid : function(preventMark){
21410         if(this.disabled){
21411             return true;
21412         }
21413         var restore = this.preventMark;
21414         this.preventMark = preventMark === true;
21415         var v = this.validateValue(this.processValue(this.getRawValue()));
21416         this.preventMark = restore;
21417         return v;
21418     },
21419
21420     /**
21421      * Validates the field value
21422      * @return {Boolean} True if the value is valid, else false
21423      */
21424     validate : function(){
21425         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21426             this.clearInvalid();
21427             return true;
21428         }
21429         return false;
21430     },
21431
21432     processValue : function(value){
21433         return value;
21434     },
21435
21436     // private
21437     // Subclasses should provide the validation implementation by overriding this
21438     validateValue : function(value){
21439         return true;
21440     },
21441
21442     /**
21443      * Mark this field as invalid
21444      * @param {String} msg The validation message
21445      */
21446     markInvalid : function(msg){
21447         if(!this.rendered || this.preventMark){ // not rendered
21448             return;
21449         }
21450         this.el.addClass(this.invalidClass);
21451         msg = msg || this.invalidText;
21452         switch(this.msgTarget){
21453             case 'qtip':
21454                 this.el.dom.qtip = msg;
21455                 this.el.dom.qclass = 'x-form-invalid-tip';
21456                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21457                     Roo.QuickTips.enable();
21458                 }
21459                 break;
21460             case 'title':
21461                 this.el.dom.title = msg;
21462                 break;
21463             case 'under':
21464                 if(!this.errorEl){
21465                     var elp = this.el.findParent('.x-form-element', 5, true);
21466                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21467                     this.errorEl.setWidth(elp.getWidth(true)-20);
21468                 }
21469                 this.errorEl.update(msg);
21470                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21471                 break;
21472             case 'side':
21473                 if(!this.errorIcon){
21474                     var elp = this.el.findParent('.x-form-element', 5, true);
21475                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21476                 }
21477                 this.alignErrorIcon();
21478                 this.errorIcon.dom.qtip = msg;
21479                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21480                 this.errorIcon.show();
21481                 this.on('resize', this.alignErrorIcon, this);
21482                 break;
21483             default:
21484                 var t = Roo.getDom(this.msgTarget);
21485                 t.innerHTML = msg;
21486                 t.style.display = this.msgDisplay;
21487                 break;
21488         }
21489         this.fireEvent('invalid', this, msg);
21490     },
21491
21492     // private
21493     alignErrorIcon : function(){
21494         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21495     },
21496
21497     /**
21498      * Clear any invalid styles/messages for this field
21499      */
21500     clearInvalid : function(){
21501         if(!this.rendered || this.preventMark){ // not rendered
21502             return;
21503         }
21504         this.el.removeClass(this.invalidClass);
21505         switch(this.msgTarget){
21506             case 'qtip':
21507                 this.el.dom.qtip = '';
21508                 break;
21509             case 'title':
21510                 this.el.dom.title = '';
21511                 break;
21512             case 'under':
21513                 if(this.errorEl){
21514                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21515                 }
21516                 break;
21517             case 'side':
21518                 if(this.errorIcon){
21519                     this.errorIcon.dom.qtip = '';
21520                     this.errorIcon.hide();
21521                     this.un('resize', this.alignErrorIcon, this);
21522                 }
21523                 break;
21524             default:
21525                 var t = Roo.getDom(this.msgTarget);
21526                 t.innerHTML = '';
21527                 t.style.display = 'none';
21528                 break;
21529         }
21530         this.fireEvent('valid', this);
21531     },
21532
21533     /**
21534      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21535      * @return {Mixed} value The field value
21536      */
21537     getRawValue : function(){
21538         var v = this.el.getValue();
21539         if(v === this.emptyText){
21540             v = '';
21541         }
21542         return v;
21543     },
21544
21545     /**
21546      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21547      * @return {Mixed} value The field value
21548      */
21549     getValue : function(){
21550         var v = this.el.getValue();
21551         if(v === this.emptyText || v === undefined){
21552             v = '';
21553         }
21554         return v;
21555     },
21556
21557     /**
21558      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21559      * @param {Mixed} value The value to set
21560      */
21561     setRawValue : function(v){
21562         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21563     },
21564
21565     /**
21566      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21567      * @param {Mixed} value The value to set
21568      */
21569     setValue : function(v){
21570         this.value = v;
21571         if(this.rendered){
21572             this.el.dom.value = (v === null || v === undefined ? '' : v);
21573              this.validate();
21574         }
21575     },
21576
21577     adjustSize : function(w, h){
21578         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21579         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21580         return s;
21581     },
21582
21583     adjustWidth : function(tag, w){
21584         tag = tag.toLowerCase();
21585         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21586             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21587                 if(tag == 'input'){
21588                     return w + 2;
21589                 }
21590                 if(tag = 'textarea'){
21591                     return w-2;
21592                 }
21593             }else if(Roo.isOpera){
21594                 if(tag == 'input'){
21595                     return w + 2;
21596                 }
21597                 if(tag = 'textarea'){
21598                     return w-2;
21599                 }
21600             }
21601         }
21602         return w;
21603     }
21604 });
21605
21606
21607 // anything other than normal should be considered experimental
21608 Roo.form.Field.msgFx = {
21609     normal : {
21610         show: function(msgEl, f){
21611             msgEl.setDisplayed('block');
21612         },
21613
21614         hide : function(msgEl, f){
21615             msgEl.setDisplayed(false).update('');
21616         }
21617     },
21618
21619     slide : {
21620         show: function(msgEl, f){
21621             msgEl.slideIn('t', {stopFx:true});
21622         },
21623
21624         hide : function(msgEl, f){
21625             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21626         }
21627     },
21628
21629     slideRight : {
21630         show: function(msgEl, f){
21631             msgEl.fixDisplay();
21632             msgEl.alignTo(f.el, 'tl-tr');
21633             msgEl.slideIn('l', {stopFx:true});
21634         },
21635
21636         hide : function(msgEl, f){
21637             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21638         }
21639     }
21640 };/*
21641  * Based on:
21642  * Ext JS Library 1.1.1
21643  * Copyright(c) 2006-2007, Ext JS, LLC.
21644  *
21645  * Originally Released Under LGPL - original licence link has changed is not relivant.
21646  *
21647  * Fork - LGPL
21648  * <script type="text/javascript">
21649  */
21650  
21651
21652 /**
21653  * @class Roo.form.TextField
21654  * @extends Roo.form.Field
21655  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21656  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21657  * @constructor
21658  * Creates a new TextField
21659  * @param {Object} config Configuration options
21660  */
21661 Roo.form.TextField = function(config){
21662     Roo.form.TextField.superclass.constructor.call(this, config);
21663     this.addEvents({
21664         /**
21665          * @event autosize
21666          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21667          * according to the default logic, but this event provides a hook for the developer to apply additional
21668          * logic at runtime to resize the field if needed.
21669              * @param {Roo.form.Field} this This text field
21670              * @param {Number} width The new field width
21671              */
21672         autosize : true
21673     });
21674 };
21675
21676 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21677     /**
21678      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21679      */
21680     grow : false,
21681     /**
21682      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21683      */
21684     growMin : 30,
21685     /**
21686      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21687      */
21688     growMax : 800,
21689     /**
21690      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21691      */
21692     vtype : null,
21693     /**
21694      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21695      */
21696     maskRe : null,
21697     /**
21698      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21699      */
21700     disableKeyFilter : false,
21701     /**
21702      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21703      */
21704     allowBlank : true,
21705     /**
21706      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21707      */
21708     minLength : 0,
21709     /**
21710      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21711      */
21712     maxLength : Number.MAX_VALUE,
21713     /**
21714      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21715      */
21716     minLengthText : "The minimum length for this field is {0}",
21717     /**
21718      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21719      */
21720     maxLengthText : "The maximum length for this field is {0}",
21721     /**
21722      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21723      */
21724     selectOnFocus : false,
21725     /**
21726      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21727      */
21728     blankText : "This field is required",
21729     /**
21730      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21731      * If available, this function will be called only after the basic validators all return true, and will be passed the
21732      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21733      */
21734     validator : null,
21735     /**
21736      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21737      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21738      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21739      */
21740     regex : null,
21741     /**
21742      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21743      */
21744     regexText : "",
21745     /**
21746      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21747      */
21748     emptyText : null,
21749     /**
21750      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21751      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21752      */
21753     emptyClass : 'x-form-empty-field',
21754
21755     // private
21756     initEvents : function(){
21757         Roo.form.TextField.superclass.initEvents.call(this);
21758         if(this.validationEvent == 'keyup'){
21759             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21760             this.el.on('keyup', this.filterValidation, this);
21761         }
21762         else if(this.validationEvent !== false){
21763             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21764         }
21765         if(this.selectOnFocus || this.emptyText){
21766             this.on("focus", this.preFocus, this);
21767             if(this.emptyText){
21768                 this.on('blur', this.postBlur, this);
21769                 this.applyEmptyText();
21770             }
21771         }
21772         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21773             this.el.on("keypress", this.filterKeys, this);
21774         }
21775         if(this.grow){
21776             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21777             this.el.on("click", this.autoSize,  this);
21778         }
21779     },
21780
21781     processValue : function(value){
21782         if(this.stripCharsRe){
21783             var newValue = value.replace(this.stripCharsRe, '');
21784             if(newValue !== value){
21785                 this.setRawValue(newValue);
21786                 return newValue;
21787             }
21788         }
21789         return value;
21790     },
21791
21792     filterValidation : function(e){
21793         if(!e.isNavKeyPress()){
21794             this.validationTask.delay(this.validationDelay);
21795         }
21796     },
21797
21798     // private
21799     onKeyUp : function(e){
21800         if(!e.isNavKeyPress()){
21801             this.autoSize();
21802         }
21803     },
21804
21805     /**
21806      * Resets the current field value to the originally-loaded value and clears any validation messages.
21807      * Also adds emptyText and emptyClass if the original value was blank.
21808      */
21809     reset : function(){
21810         Roo.form.TextField.superclass.reset.call(this);
21811         this.applyEmptyText();
21812     },
21813
21814     applyEmptyText : function(){
21815         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21816             this.setRawValue(this.emptyText);
21817             this.el.addClass(this.emptyClass);
21818         }
21819     },
21820
21821     // private
21822     preFocus : function(){
21823         if(this.emptyText){
21824             if(this.el.dom.value == this.emptyText){
21825                 this.setRawValue('');
21826             }
21827             this.el.removeClass(this.emptyClass);
21828         }
21829         if(this.selectOnFocus){
21830             this.el.dom.select();
21831         }
21832     },
21833
21834     // private
21835     postBlur : function(){
21836         this.applyEmptyText();
21837     },
21838
21839     // private
21840     filterKeys : function(e){
21841         var k = e.getKey();
21842         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21843             return;
21844         }
21845         var c = e.getCharCode(), cc = String.fromCharCode(c);
21846         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21847             return;
21848         }
21849         if(!this.maskRe.test(cc)){
21850             e.stopEvent();
21851         }
21852     },
21853
21854     setValue : function(v){
21855         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21856             this.el.removeClass(this.emptyClass);
21857         }
21858         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21859         this.applyEmptyText();
21860         this.autoSize();
21861     },
21862
21863     /**
21864      * Validates a value according to the field's validation rules and marks the field as invalid
21865      * if the validation fails
21866      * @param {Mixed} value The value to validate
21867      * @return {Boolean} True if the value is valid, else false
21868      */
21869     validateValue : function(value){
21870         if(value.length < 1 || value === this.emptyText){ // if it's blank
21871              if(this.allowBlank){
21872                 this.clearInvalid();
21873                 return true;
21874              }else{
21875                 this.markInvalid(this.blankText);
21876                 return false;
21877              }
21878         }
21879         if(value.length < this.minLength){
21880             this.markInvalid(String.format(this.minLengthText, this.minLength));
21881             return false;
21882         }
21883         if(value.length > this.maxLength){
21884             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21885             return false;
21886         }
21887         if(this.vtype){
21888             var vt = Roo.form.VTypes;
21889             if(!vt[this.vtype](value, this)){
21890                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21891                 return false;
21892             }
21893         }
21894         if(typeof this.validator == "function"){
21895             var msg = this.validator(value);
21896             if(msg !== true){
21897                 this.markInvalid(msg);
21898                 return false;
21899             }
21900         }
21901         if(this.regex && !this.regex.test(value)){
21902             this.markInvalid(this.regexText);
21903             return false;
21904         }
21905         return true;
21906     },
21907
21908     /**
21909      * Selects text in this field
21910      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21911      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21912      */
21913     selectText : function(start, end){
21914         var v = this.getRawValue();
21915         if(v.length > 0){
21916             start = start === undefined ? 0 : start;
21917             end = end === undefined ? v.length : end;
21918             var d = this.el.dom;
21919             if(d.setSelectionRange){
21920                 d.setSelectionRange(start, end);
21921             }else if(d.createTextRange){
21922                 var range = d.createTextRange();
21923                 range.moveStart("character", start);
21924                 range.moveEnd("character", v.length-end);
21925                 range.select();
21926             }
21927         }
21928     },
21929
21930     /**
21931      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21932      * This only takes effect if grow = true, and fires the autosize event.
21933      */
21934     autoSize : function(){
21935         if(!this.grow || !this.rendered){
21936             return;
21937         }
21938         if(!this.metrics){
21939             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21940         }
21941         var el = this.el;
21942         var v = el.dom.value;
21943         var d = document.createElement('div');
21944         d.appendChild(document.createTextNode(v));
21945         v = d.innerHTML;
21946         d = null;
21947         v += "&#160;";
21948         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21949         this.el.setWidth(w);
21950         this.fireEvent("autosize", this, w);
21951     }
21952 });/*
21953  * Based on:
21954  * Ext JS Library 1.1.1
21955  * Copyright(c) 2006-2007, Ext JS, LLC.
21956  *
21957  * Originally Released Under LGPL - original licence link has changed is not relivant.
21958  *
21959  * Fork - LGPL
21960  * <script type="text/javascript">
21961  */
21962  
21963 /**
21964  * @class Roo.form.Hidden
21965  * @extends Roo.form.TextField
21966  * Simple Hidden element used on forms 
21967  * 
21968  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21969  * 
21970  * @constructor
21971  * Creates a new Hidden form element.
21972  * @param {Object} config Configuration options
21973  */
21974
21975
21976
21977 // easy hidden field...
21978 Roo.form.Hidden = function(config){
21979     Roo.form.Hidden.superclass.constructor.call(this, config);
21980 };
21981   
21982 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21983     fieldLabel:      '',
21984     inputType:      'hidden',
21985     width:          50,
21986     allowBlank:     true,
21987     labelSeparator: '',
21988     hidden:         true,
21989     itemCls :       'x-form-item-display-none'
21990
21991
21992 });
21993
21994
21995 /*
21996  * Based on:
21997  * Ext JS Library 1.1.1
21998  * Copyright(c) 2006-2007, Ext JS, LLC.
21999  *
22000  * Originally Released Under LGPL - original licence link has changed is not relivant.
22001  *
22002  * Fork - LGPL
22003  * <script type="text/javascript">
22004  */
22005  
22006 /**
22007  * @class Roo.form.TriggerField
22008  * @extends Roo.form.TextField
22009  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22010  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22011  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22012  * for which you can provide a custom implementation.  For example:
22013  * <pre><code>
22014 var trigger = new Roo.form.TriggerField();
22015 trigger.onTriggerClick = myTriggerFn;
22016 trigger.applyTo('my-field');
22017 </code></pre>
22018  *
22019  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22020  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22021  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22022  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22023  * @constructor
22024  * Create a new TriggerField.
22025  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22026  * to the base TextField)
22027  */
22028 Roo.form.TriggerField = function(config){
22029     this.mimicing = false;
22030     Roo.form.TriggerField.superclass.constructor.call(this, config);
22031 };
22032
22033 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22034     /**
22035      * @cfg {String} triggerClass A CSS class to apply to the trigger
22036      */
22037     /**
22038      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22039      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22040      */
22041     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22042     /**
22043      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22044      */
22045     hideTrigger:false,
22046
22047     /** @cfg {Boolean} grow @hide */
22048     /** @cfg {Number} growMin @hide */
22049     /** @cfg {Number} growMax @hide */
22050
22051     /**
22052      * @hide 
22053      * @method
22054      */
22055     autoSize: Roo.emptyFn,
22056     // private
22057     monitorTab : true,
22058     // private
22059     deferHeight : true,
22060
22061     
22062     actionMode : 'wrap',
22063     // private
22064     onResize : function(w, h){
22065         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22066         if(typeof w == 'number'){
22067             var x = w - this.trigger.getWidth();
22068             this.el.setWidth(this.adjustWidth('input', x));
22069             this.trigger.setStyle('left', x+'px');
22070         }
22071     },
22072
22073     // private
22074     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22075
22076     // private
22077     getResizeEl : function(){
22078         return this.wrap;
22079     },
22080
22081     // private
22082     getPositionEl : function(){
22083         return this.wrap;
22084     },
22085
22086     // private
22087     alignErrorIcon : function(){
22088         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22089     },
22090
22091     // private
22092     onRender : function(ct, position){
22093         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22094         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22095         this.trigger = this.wrap.createChild(this.triggerConfig ||
22096                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22097         if(this.hideTrigger){
22098             this.trigger.setDisplayed(false);
22099         }
22100         this.initTrigger();
22101         if(!this.width){
22102             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22103         }
22104     },
22105
22106     // private
22107     initTrigger : function(){
22108         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22109         this.trigger.addClassOnOver('x-form-trigger-over');
22110         this.trigger.addClassOnClick('x-form-trigger-click');
22111     },
22112
22113     // private
22114     onDestroy : function(){
22115         if(this.trigger){
22116             this.trigger.removeAllListeners();
22117             this.trigger.remove();
22118         }
22119         if(this.wrap){
22120             this.wrap.remove();
22121         }
22122         Roo.form.TriggerField.superclass.onDestroy.call(this);
22123     },
22124
22125     // private
22126     onFocus : function(){
22127         Roo.form.TriggerField.superclass.onFocus.call(this);
22128         if(!this.mimicing){
22129             this.wrap.addClass('x-trigger-wrap-focus');
22130             this.mimicing = true;
22131             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22132             if(this.monitorTab){
22133                 this.el.on("keydown", this.checkTab, this);
22134             }
22135         }
22136     },
22137
22138     // private
22139     checkTab : function(e){
22140         if(e.getKey() == e.TAB){
22141             this.triggerBlur();
22142         }
22143     },
22144
22145     // private
22146     onBlur : function(){
22147         // do nothing
22148     },
22149
22150     // private
22151     mimicBlur : function(e, t){
22152         if(!this.wrap.contains(t) && this.validateBlur()){
22153             this.triggerBlur();
22154         }
22155     },
22156
22157     // private
22158     triggerBlur : function(){
22159         this.mimicing = false;
22160         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22161         if(this.monitorTab){
22162             this.el.un("keydown", this.checkTab, this);
22163         }
22164         this.wrap.removeClass('x-trigger-wrap-focus');
22165         Roo.form.TriggerField.superclass.onBlur.call(this);
22166     },
22167
22168     // private
22169     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22170     validateBlur : function(e, t){
22171         return true;
22172     },
22173
22174     // private
22175     onDisable : function(){
22176         Roo.form.TriggerField.superclass.onDisable.call(this);
22177         if(this.wrap){
22178             this.wrap.addClass('x-item-disabled');
22179         }
22180     },
22181
22182     // private
22183     onEnable : function(){
22184         Roo.form.TriggerField.superclass.onEnable.call(this);
22185         if(this.wrap){
22186             this.wrap.removeClass('x-item-disabled');
22187         }
22188     },
22189
22190     // private
22191     onShow : function(){
22192         var ae = this.getActionEl();
22193         
22194         if(ae){
22195             ae.dom.style.display = '';
22196             ae.dom.style.visibility = 'visible';
22197         }
22198     },
22199
22200     // private
22201     
22202     onHide : function(){
22203         var ae = this.getActionEl();
22204         ae.dom.style.display = 'none';
22205     },
22206
22207     /**
22208      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22209      * by an implementing function.
22210      * @method
22211      * @param {EventObject} e
22212      */
22213     onTriggerClick : Roo.emptyFn
22214 });
22215
22216 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22217 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22218 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22219 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22220     initComponent : function(){
22221         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22222
22223         this.triggerConfig = {
22224             tag:'span', cls:'x-form-twin-triggers', cn:[
22225             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22226             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22227         ]};
22228     },
22229
22230     getTrigger : function(index){
22231         return this.triggers[index];
22232     },
22233
22234     initTrigger : function(){
22235         var ts = this.trigger.select('.x-form-trigger', true);
22236         this.wrap.setStyle('overflow', 'hidden');
22237         var triggerField = this;
22238         ts.each(function(t, all, index){
22239             t.hide = function(){
22240                 var w = triggerField.wrap.getWidth();
22241                 this.dom.style.display = 'none';
22242                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22243             };
22244             t.show = function(){
22245                 var w = triggerField.wrap.getWidth();
22246                 this.dom.style.display = '';
22247                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22248             };
22249             var triggerIndex = 'Trigger'+(index+1);
22250
22251             if(this['hide'+triggerIndex]){
22252                 t.dom.style.display = 'none';
22253             }
22254             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22255             t.addClassOnOver('x-form-trigger-over');
22256             t.addClassOnClick('x-form-trigger-click');
22257         }, this);
22258         this.triggers = ts.elements;
22259     },
22260
22261     onTrigger1Click : Roo.emptyFn,
22262     onTrigger2Click : Roo.emptyFn
22263 });/*
22264  * Based on:
22265  * Ext JS Library 1.1.1
22266  * Copyright(c) 2006-2007, Ext JS, LLC.
22267  *
22268  * Originally Released Under LGPL - original licence link has changed is not relivant.
22269  *
22270  * Fork - LGPL
22271  * <script type="text/javascript">
22272  */
22273  
22274 /**
22275  * @class Roo.form.TextArea
22276  * @extends Roo.form.TextField
22277  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22278  * support for auto-sizing.
22279  * @constructor
22280  * Creates a new TextArea
22281  * @param {Object} config Configuration options
22282  */
22283 Roo.form.TextArea = function(config){
22284     Roo.form.TextArea.superclass.constructor.call(this, config);
22285     // these are provided exchanges for backwards compat
22286     // minHeight/maxHeight were replaced by growMin/growMax to be
22287     // compatible with TextField growing config values
22288     if(this.minHeight !== undefined){
22289         this.growMin = this.minHeight;
22290     }
22291     if(this.maxHeight !== undefined){
22292         this.growMax = this.maxHeight;
22293     }
22294 };
22295
22296 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22297     /**
22298      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22299      */
22300     growMin : 60,
22301     /**
22302      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22303      */
22304     growMax: 1000,
22305     /**
22306      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22307      * in the field (equivalent to setting overflow: hidden, defaults to false)
22308      */
22309     preventScrollbars: false,
22310     /**
22311      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22312      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22313      */
22314
22315     // private
22316     onRender : function(ct, position){
22317         if(!this.el){
22318             this.defaultAutoCreate = {
22319                 tag: "textarea",
22320                 style:"width:300px;height:60px;",
22321                 autocomplete: "off"
22322             };
22323         }
22324         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22325         if(this.grow){
22326             this.textSizeEl = Roo.DomHelper.append(document.body, {
22327                 tag: "pre", cls: "x-form-grow-sizer"
22328             });
22329             if(this.preventScrollbars){
22330                 this.el.setStyle("overflow", "hidden");
22331             }
22332             this.el.setHeight(this.growMin);
22333         }
22334     },
22335
22336     onDestroy : function(){
22337         if(this.textSizeEl){
22338             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22339         }
22340         Roo.form.TextArea.superclass.onDestroy.call(this);
22341     },
22342
22343     // private
22344     onKeyUp : function(e){
22345         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22346             this.autoSize();
22347         }
22348     },
22349
22350     /**
22351      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22352      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22353      */
22354     autoSize : function(){
22355         if(!this.grow || !this.textSizeEl){
22356             return;
22357         }
22358         var el = this.el;
22359         var v = el.dom.value;
22360         var ts = this.textSizeEl;
22361
22362         ts.innerHTML = '';
22363         ts.appendChild(document.createTextNode(v));
22364         v = ts.innerHTML;
22365
22366         Roo.fly(ts).setWidth(this.el.getWidth());
22367         if(v.length < 1){
22368             v = "&#160;&#160;";
22369         }else{
22370             if(Roo.isIE){
22371                 v = v.replace(/\n/g, '<p>&#160;</p>');
22372             }
22373             v += "&#160;\n&#160;";
22374         }
22375         ts.innerHTML = v;
22376         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22377         if(h != this.lastHeight){
22378             this.lastHeight = h;
22379             this.el.setHeight(h);
22380             this.fireEvent("autosize", this, h);
22381         }
22382     }
22383 });/*
22384  * Based on:
22385  * Ext JS Library 1.1.1
22386  * Copyright(c) 2006-2007, Ext JS, LLC.
22387  *
22388  * Originally Released Under LGPL - original licence link has changed is not relivant.
22389  *
22390  * Fork - LGPL
22391  * <script type="text/javascript">
22392  */
22393  
22394
22395 /**
22396  * @class Roo.form.NumberField
22397  * @extends Roo.form.TextField
22398  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22399  * @constructor
22400  * Creates a new NumberField
22401  * @param {Object} config Configuration options
22402  */
22403 Roo.form.NumberField = function(config){
22404     Roo.form.NumberField.superclass.constructor.call(this, config);
22405 };
22406
22407 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22408     /**
22409      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22410      */
22411     fieldClass: "x-form-field x-form-num-field",
22412     /**
22413      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22414      */
22415     allowDecimals : true,
22416     /**
22417      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22418      */
22419     decimalSeparator : ".",
22420     /**
22421      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22422      */
22423     decimalPrecision : 2,
22424     /**
22425      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22426      */
22427     allowNegative : true,
22428     /**
22429      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22430      */
22431     minValue : Number.NEGATIVE_INFINITY,
22432     /**
22433      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22434      */
22435     maxValue : Number.MAX_VALUE,
22436     /**
22437      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22438      */
22439     minText : "The minimum value for this field is {0}",
22440     /**
22441      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22442      */
22443     maxText : "The maximum value for this field is {0}",
22444     /**
22445      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22446      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22447      */
22448     nanText : "{0} is not a valid number",
22449
22450     // private
22451     initEvents : function(){
22452         Roo.form.NumberField.superclass.initEvents.call(this);
22453         var allowed = "0123456789";
22454         if(this.allowDecimals){
22455             allowed += this.decimalSeparator;
22456         }
22457         if(this.allowNegative){
22458             allowed += "-";
22459         }
22460         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22461         var keyPress = function(e){
22462             var k = e.getKey();
22463             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22464                 return;
22465             }
22466             var c = e.getCharCode();
22467             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22468                 e.stopEvent();
22469             }
22470         };
22471         this.el.on("keypress", keyPress, this);
22472     },
22473
22474     // private
22475     validateValue : function(value){
22476         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22477             return false;
22478         }
22479         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22480              return true;
22481         }
22482         var num = this.parseValue(value);
22483         if(isNaN(num)){
22484             this.markInvalid(String.format(this.nanText, value));
22485             return false;
22486         }
22487         if(num < this.minValue){
22488             this.markInvalid(String.format(this.minText, this.minValue));
22489             return false;
22490         }
22491         if(num > this.maxValue){
22492             this.markInvalid(String.format(this.maxText, this.maxValue));
22493             return false;
22494         }
22495         return true;
22496     },
22497
22498     getValue : function(){
22499         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22500     },
22501
22502     // private
22503     parseValue : function(value){
22504         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22505         return isNaN(value) ? '' : value;
22506     },
22507
22508     // private
22509     fixPrecision : function(value){
22510         var nan = isNaN(value);
22511         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22512             return nan ? '' : value;
22513         }
22514         return parseFloat(value).toFixed(this.decimalPrecision);
22515     },
22516
22517     setValue : function(v){
22518         v = this.fixPrecision(v);
22519         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22520     },
22521
22522     // private
22523     decimalPrecisionFcn : function(v){
22524         return Math.floor(v);
22525     },
22526
22527     beforeBlur : function(){
22528         var v = this.parseValue(this.getRawValue());
22529         if(v){
22530             this.setValue(v);
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  * @class Roo.form.DateField
22546  * @extends Roo.form.TriggerField
22547  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22548 * @constructor
22549 * Create a new DateField
22550 * @param {Object} config
22551  */
22552 Roo.form.DateField = function(config){
22553     Roo.form.DateField.superclass.constructor.call(this, config);
22554     
22555       this.addEvents({
22556          
22557         /**
22558          * @event select
22559          * Fires when a date is selected
22560              * @param {Roo.form.DateField} combo This combo box
22561              * @param {Date} date The date selected
22562              */
22563         'select' : true
22564          
22565     });
22566     
22567     
22568     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22569     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22570     this.ddMatch = null;
22571     if(this.disabledDates){
22572         var dd = this.disabledDates;
22573         var re = "(?:";
22574         for(var i = 0; i < dd.length; i++){
22575             re += dd[i];
22576             if(i != dd.length-1) re += "|";
22577         }
22578         this.ddMatch = new RegExp(re + ")");
22579     }
22580 };
22581
22582 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22583     /**
22584      * @cfg {String} format
22585      * The default date format string which can be overriden for localization support.  The format must be
22586      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22587      */
22588     format : "m/d/y",
22589     /**
22590      * @cfg {String} altFormats
22591      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22592      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22593      */
22594     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22595     /**
22596      * @cfg {Array} disabledDays
22597      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22598      */
22599     disabledDays : null,
22600     /**
22601      * @cfg {String} disabledDaysText
22602      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22603      */
22604     disabledDaysText : "Disabled",
22605     /**
22606      * @cfg {Array} disabledDates
22607      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22608      * expression so they are very powerful. Some examples:
22609      * <ul>
22610      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22611      * <li>["03/08", "09/16"] would disable those days for every year</li>
22612      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22613      * <li>["03/../2006"] would disable every day in March 2006</li>
22614      * <li>["^03"] would disable every day in every March</li>
22615      * </ul>
22616      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22617      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22618      */
22619     disabledDates : null,
22620     /**
22621      * @cfg {String} disabledDatesText
22622      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22623      */
22624     disabledDatesText : "Disabled",
22625     /**
22626      * @cfg {Date/String} minValue
22627      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22628      * valid format (defaults to null).
22629      */
22630     minValue : null,
22631     /**
22632      * @cfg {Date/String} maxValue
22633      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22634      * valid format (defaults to null).
22635      */
22636     maxValue : null,
22637     /**
22638      * @cfg {String} minText
22639      * The error text to display when the date in the cell is before minValue (defaults to
22640      * 'The date in this field must be after {minValue}').
22641      */
22642     minText : "The date in this field must be equal to or after {0}",
22643     /**
22644      * @cfg {String} maxText
22645      * The error text to display when the date in the cell is after maxValue (defaults to
22646      * 'The date in this field must be before {maxValue}').
22647      */
22648     maxText : "The date in this field must be equal to or before {0}",
22649     /**
22650      * @cfg {String} invalidText
22651      * The error text to display when the date in the field is invalid (defaults to
22652      * '{value} is not a valid date - it must be in the format {format}').
22653      */
22654     invalidText : "{0} is not a valid date - it must be in the format {1}",
22655     /**
22656      * @cfg {String} triggerClass
22657      * An additional CSS class used to style the trigger button.  The trigger will always get the
22658      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22659      * which displays a calendar icon).
22660      */
22661     triggerClass : 'x-form-date-trigger',
22662     
22663
22664     /**
22665      * @cfg {bool} useIso
22666      * if enabled, then the date field will use a hidden field to store the 
22667      * real value as iso formated date. default (false)
22668      */ 
22669     useIso : false,
22670     /**
22671      * @cfg {String/Object} autoCreate
22672      * A DomHelper element spec, or true for a default element spec (defaults to
22673      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22674      */ 
22675     // private
22676     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22677     
22678     // private
22679     hiddenField: false,
22680     
22681     onRender : function(ct, position)
22682     {
22683         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22684         if (this.useIso) {
22685             this.el.dom.removeAttribute('name'); 
22686             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22687                     'before', true);
22688             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22689             // prevent input submission
22690             this.hiddenName = this.name;
22691         }
22692             
22693             
22694     },
22695     
22696     // private
22697     validateValue : function(value)
22698     {
22699         value = this.formatDate(value);
22700         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22701             return false;
22702         }
22703         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22704              return true;
22705         }
22706         var svalue = value;
22707         value = this.parseDate(value);
22708         if(!value){
22709             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22710             return false;
22711         }
22712         var time = value.getTime();
22713         if(this.minValue && time < this.minValue.getTime()){
22714             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22715             return false;
22716         }
22717         if(this.maxValue && time > this.maxValue.getTime()){
22718             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22719             return false;
22720         }
22721         if(this.disabledDays){
22722             var day = value.getDay();
22723             for(var i = 0; i < this.disabledDays.length; i++) {
22724                 if(day === this.disabledDays[i]){
22725                     this.markInvalid(this.disabledDaysText);
22726                     return false;
22727                 }
22728             }
22729         }
22730         var fvalue = this.formatDate(value);
22731         if(this.ddMatch && this.ddMatch.test(fvalue)){
22732             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22733             return false;
22734         }
22735         return true;
22736     },
22737
22738     // private
22739     // Provides logic to override the default TriggerField.validateBlur which just returns true
22740     validateBlur : function(){
22741         return !this.menu || !this.menu.isVisible();
22742     },
22743
22744     /**
22745      * Returns the current date value of the date field.
22746      * @return {Date} The date value
22747      */
22748     getValue : function(){
22749         
22750         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22751     },
22752
22753     /**
22754      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22755      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22756      * (the default format used is "m/d/y").
22757      * <br />Usage:
22758      * <pre><code>
22759 //All of these calls set the same date value (May 4, 2006)
22760
22761 //Pass a date object:
22762 var dt = new Date('5/4/06');
22763 dateField.setValue(dt);
22764
22765 //Pass a date string (default format):
22766 dateField.setValue('5/4/06');
22767
22768 //Pass a date string (custom format):
22769 dateField.format = 'Y-m-d';
22770 dateField.setValue('2006-5-4');
22771 </code></pre>
22772      * @param {String/Date} date The date or valid date string
22773      */
22774     setValue : function(date){
22775         if (this.hiddenField) {
22776             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22777         }
22778         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22779     },
22780
22781     // private
22782     parseDate : function(value){
22783         if(!value || value instanceof Date){
22784             return value;
22785         }
22786         var v = Date.parseDate(value, this.format);
22787         if(!v && this.altFormats){
22788             if(!this.altFormatsArray){
22789                 this.altFormatsArray = this.altFormats.split("|");
22790             }
22791             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22792                 v = Date.parseDate(value, this.altFormatsArray[i]);
22793             }
22794         }
22795         return v;
22796     },
22797
22798     // private
22799     formatDate : function(date, fmt){
22800         return (!date || !(date instanceof Date)) ?
22801                date : date.dateFormat(fmt || this.format);
22802     },
22803
22804     // private
22805     menuListeners : {
22806         select: function(m, d){
22807             this.setValue(d);
22808             this.fireEvent('select', this, d);
22809         },
22810         show : function(){ // retain focus styling
22811             this.onFocus();
22812         },
22813         hide : function(){
22814             this.focus.defer(10, this);
22815             var ml = this.menuListeners;
22816             this.menu.un("select", ml.select,  this);
22817             this.menu.un("show", ml.show,  this);
22818             this.menu.un("hide", ml.hide,  this);
22819         }
22820     },
22821
22822     // private
22823     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22824     onTriggerClick : function(){
22825         if(this.disabled){
22826             return;
22827         }
22828         if(this.menu == null){
22829             this.menu = new Roo.menu.DateMenu();
22830         }
22831         Roo.apply(this.menu.picker,  {
22832             showClear: this.allowBlank,
22833             minDate : this.minValue,
22834             maxDate : this.maxValue,
22835             disabledDatesRE : this.ddMatch,
22836             disabledDatesText : this.disabledDatesText,
22837             disabledDays : this.disabledDays,
22838             disabledDaysText : this.disabledDaysText,
22839             format : this.format,
22840             minText : String.format(this.minText, this.formatDate(this.minValue)),
22841             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22842         });
22843         this.menu.on(Roo.apply({}, this.menuListeners, {
22844             scope:this
22845         }));
22846         this.menu.picker.setValue(this.getValue() || new Date());
22847         this.menu.show(this.el, "tl-bl?");
22848     },
22849
22850     beforeBlur : function(){
22851         var v = this.parseDate(this.getRawValue());
22852         if(v){
22853             this.setValue(v);
22854         }
22855     }
22856
22857     /** @cfg {Boolean} grow @hide */
22858     /** @cfg {Number} growMin @hide */
22859     /** @cfg {Number} growMax @hide */
22860     /**
22861      * @hide
22862      * @method autoSize
22863      */
22864 });/*
22865  * Based on:
22866  * Ext JS Library 1.1.1
22867  * Copyright(c) 2006-2007, Ext JS, LLC.
22868  *
22869  * Originally Released Under LGPL - original licence link has changed is not relivant.
22870  *
22871  * Fork - LGPL
22872  * <script type="text/javascript">
22873  */
22874  
22875
22876 /**
22877  * @class Roo.form.ComboBox
22878  * @extends Roo.form.TriggerField
22879  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22880  * @constructor
22881  * Create a new ComboBox.
22882  * @param {Object} config Configuration options
22883  */
22884 Roo.form.ComboBox = function(config){
22885     Roo.form.ComboBox.superclass.constructor.call(this, config);
22886     this.addEvents({
22887         /**
22888          * @event expand
22889          * Fires when the dropdown list is expanded
22890              * @param {Roo.form.ComboBox} combo This combo box
22891              */
22892         'expand' : true,
22893         /**
22894          * @event collapse
22895          * Fires when the dropdown list is collapsed
22896              * @param {Roo.form.ComboBox} combo This combo box
22897              */
22898         'collapse' : true,
22899         /**
22900          * @event beforeselect
22901          * Fires before a list item is selected. Return false to cancel the selection.
22902              * @param {Roo.form.ComboBox} combo This combo box
22903              * @param {Roo.data.Record} record The data record returned from the underlying store
22904              * @param {Number} index The index of the selected item in the dropdown list
22905              */
22906         'beforeselect' : true,
22907         /**
22908          * @event select
22909          * Fires when a list item is selected
22910              * @param {Roo.form.ComboBox} combo This combo box
22911              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22912              * @param {Number} index The index of the selected item in the dropdown list
22913              */
22914         'select' : true,
22915         /**
22916          * @event beforequery
22917          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22918          * The event object passed has these properties:
22919              * @param {Roo.form.ComboBox} combo This combo box
22920              * @param {String} query The query
22921              * @param {Boolean} forceAll true to force "all" query
22922              * @param {Boolean} cancel true to cancel the query
22923              * @param {Object} e The query event object
22924              */
22925         'beforequery': true,
22926          /**
22927          * @event add
22928          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22929              * @param {Roo.form.ComboBox} combo This combo box
22930              */
22931         'add' : true,
22932         /**
22933          * @event edit
22934          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22935              * @param {Roo.form.ComboBox} combo This combo box
22936              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22937              */
22938         'edit' : true
22939         
22940         
22941     });
22942     if(this.transform){
22943         this.allowDomMove = false;
22944         var s = Roo.getDom(this.transform);
22945         if(!this.hiddenName){
22946             this.hiddenName = s.name;
22947         }
22948         if(!this.store){
22949             this.mode = 'local';
22950             var d = [], opts = s.options;
22951             for(var i = 0, len = opts.length;i < len; i++){
22952                 var o = opts[i];
22953                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22954                 if(o.selected) {
22955                     this.value = value;
22956                 }
22957                 d.push([value, o.text]);
22958             }
22959             this.store = new Roo.data.SimpleStore({
22960                 'id': 0,
22961                 fields: ['value', 'text'],
22962                 data : d
22963             });
22964             this.valueField = 'value';
22965             this.displayField = 'text';
22966         }
22967         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22968         if(!this.lazyRender){
22969             this.target = true;
22970             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22971             s.parentNode.removeChild(s); // remove it
22972             this.render(this.el.parentNode);
22973         }else{
22974             s.parentNode.removeChild(s); // remove it
22975         }
22976
22977     }
22978     if (this.store) {
22979         this.store = Roo.factory(this.store, Roo.data);
22980     }
22981     
22982     this.selectedIndex = -1;
22983     if(this.mode == 'local'){
22984         if(config.queryDelay === undefined){
22985             this.queryDelay = 10;
22986         }
22987         if(config.minChars === undefined){
22988             this.minChars = 0;
22989         }
22990     }
22991 };
22992
22993 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22994     /**
22995      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22996      */
22997     /**
22998      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22999      * rendering into an Roo.Editor, defaults to false)
23000      */
23001     /**
23002      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23003      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23004      */
23005     /**
23006      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23007      */
23008     /**
23009      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23010      * the dropdown list (defaults to undefined, with no header element)
23011      */
23012
23013      /**
23014      * @cfg {String/Roo.Template} tpl The template to use to render the output
23015      */
23016      
23017     // private
23018     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23019     /**
23020      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23021      */
23022     listWidth: undefined,
23023     /**
23024      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23025      * mode = 'remote' or 'text' if mode = 'local')
23026      */
23027     displayField: undefined,
23028     /**
23029      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23030      * mode = 'remote' or 'value' if mode = 'local'). 
23031      * Note: use of a valueField requires the user make a selection
23032      * in order for a value to be mapped.
23033      */
23034     valueField: undefined,
23035     
23036     
23037     /**
23038      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23039      * field's data value (defaults to the underlying DOM element's name)
23040      */
23041     hiddenName: undefined,
23042     /**
23043      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23044      */
23045     listClass: '',
23046     /**
23047      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23048      */
23049     selectedClass: 'x-combo-selected',
23050     /**
23051      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23052      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23053      * which displays a downward arrow icon).
23054      */
23055     triggerClass : 'x-form-arrow-trigger',
23056     /**
23057      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23058      */
23059     shadow:'sides',
23060     /**
23061      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23062      * anchor positions (defaults to 'tl-bl')
23063      */
23064     listAlign: 'tl-bl?',
23065     /**
23066      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23067      */
23068     maxHeight: 300,
23069     /**
23070      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23071      * query specified by the allQuery config option (defaults to 'query')
23072      */
23073     triggerAction: 'query',
23074     /**
23075      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23076      * (defaults to 4, does not apply if editable = false)
23077      */
23078     minChars : 4,
23079     /**
23080      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23081      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23082      */
23083     typeAhead: false,
23084     /**
23085      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23086      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23087      */
23088     queryDelay: 500,
23089     /**
23090      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23091      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23092      */
23093     pageSize: 0,
23094     /**
23095      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23096      * when editable = true (defaults to false)
23097      */
23098     selectOnFocus:false,
23099     /**
23100      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23101      */
23102     queryParam: 'query',
23103     /**
23104      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23105      * when mode = 'remote' (defaults to 'Loading...')
23106      */
23107     loadingText: 'Loading...',
23108     /**
23109      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23110      */
23111     resizable: false,
23112     /**
23113      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23114      */
23115     handleHeight : 8,
23116     /**
23117      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23118      * traditional select (defaults to true)
23119      */
23120     editable: true,
23121     /**
23122      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23123      */
23124     allQuery: '',
23125     /**
23126      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23127      */
23128     mode: 'remote',
23129     /**
23130      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23131      * listWidth has a higher value)
23132      */
23133     minListWidth : 70,
23134     /**
23135      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23136      * allow the user to set arbitrary text into the field (defaults to false)
23137      */
23138     forceSelection:false,
23139     /**
23140      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23141      * if typeAhead = true (defaults to 250)
23142      */
23143     typeAheadDelay : 250,
23144     /**
23145      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23146      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23147      */
23148     valueNotFoundText : undefined,
23149     /**
23150      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23151      */
23152     blockFocus : false,
23153     
23154     /**
23155      * @cfg {Boolean} disableClear Disable showing of clear button.
23156      */
23157     disableClear : false,
23158     /**
23159      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23160      */
23161     alwaysQuery : false,
23162     
23163     //private
23164     addicon : false,
23165     editicon: false,
23166     
23167     // element that contains real text value.. (when hidden is used..)
23168      
23169     // private
23170     onRender : function(ct, position){
23171         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23172         if(this.hiddenName){
23173             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23174                     'before', true);
23175             this.hiddenField.value =
23176                 this.hiddenValue !== undefined ? this.hiddenValue :
23177                 this.value !== undefined ? this.value : '';
23178
23179             // prevent input submission
23180             this.el.dom.removeAttribute('name');
23181              
23182              
23183         }
23184         if(Roo.isGecko){
23185             this.el.dom.setAttribute('autocomplete', 'off');
23186         }
23187
23188         var cls = 'x-combo-list';
23189
23190         this.list = new Roo.Layer({
23191             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23192         });
23193
23194         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23195         this.list.setWidth(lw);
23196         this.list.swallowEvent('mousewheel');
23197         this.assetHeight = 0;
23198
23199         if(this.title){
23200             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23201             this.assetHeight += this.header.getHeight();
23202         }
23203
23204         this.innerList = this.list.createChild({cls:cls+'-inner'});
23205         this.innerList.on('mouseover', this.onViewOver, this);
23206         this.innerList.on('mousemove', this.onViewMove, this);
23207         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23208         
23209         if(this.allowBlank && !this.pageSize && !this.disableClear){
23210             this.footer = this.list.createChild({cls:cls+'-ft'});
23211             this.pageTb = new Roo.Toolbar(this.footer);
23212            
23213         }
23214         if(this.pageSize){
23215             this.footer = this.list.createChild({cls:cls+'-ft'});
23216             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23217                     {pageSize: this.pageSize});
23218             
23219         }
23220         
23221         if (this.pageTb && this.allowBlank && !this.disableClear) {
23222             var _this = this;
23223             this.pageTb.add(new Roo.Toolbar.Fill(), {
23224                 cls: 'x-btn-icon x-btn-clear',
23225                 text: '&#160;',
23226                 handler: function()
23227                 {
23228                     _this.collapse();
23229                     _this.clearValue();
23230                     _this.onSelect(false, -1);
23231                 }
23232             });
23233         }
23234         if (this.footer) {
23235             this.assetHeight += this.footer.getHeight();
23236         }
23237         
23238
23239         if(!this.tpl){
23240             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23241         }
23242
23243         this.view = new Roo.View(this.innerList, this.tpl, {
23244             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23245         });
23246
23247         this.view.on('click', this.onViewClick, this);
23248
23249         this.store.on('beforeload', this.onBeforeLoad, this);
23250         this.store.on('load', this.onLoad, this);
23251         this.store.on('loadexception', this.onLoadException, this);
23252
23253         if(this.resizable){
23254             this.resizer = new Roo.Resizable(this.list,  {
23255                pinned:true, handles:'se'
23256             });
23257             this.resizer.on('resize', function(r, w, h){
23258                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23259                 this.listWidth = w;
23260                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23261                 this.restrictHeight();
23262             }, this);
23263             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23264         }
23265         if(!this.editable){
23266             this.editable = true;
23267             this.setEditable(false);
23268         }  
23269         
23270         
23271         if (typeof(this.events.add.listeners) != 'undefined') {
23272             
23273             this.addicon = this.wrap.createChild(
23274                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23275        
23276             this.addicon.on('click', function(e) {
23277                 this.fireEvent('add', this);
23278             }, this);
23279         }
23280         if (typeof(this.events.edit.listeners) != 'undefined') {
23281             
23282             this.editicon = this.wrap.createChild(
23283                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23284             if (this.addicon) {
23285                 this.editicon.setStyle('margin-left', '40px');
23286             }
23287             this.editicon.on('click', function(e) {
23288                 
23289                 // we fire even  if inothing is selected..
23290                 this.fireEvent('edit', this, this.lastData );
23291                 
23292             }, this);
23293         }
23294         
23295         
23296         
23297     },
23298
23299     // private
23300     initEvents : function(){
23301         Roo.form.ComboBox.superclass.initEvents.call(this);
23302
23303         this.keyNav = new Roo.KeyNav(this.el, {
23304             "up" : function(e){
23305                 this.inKeyMode = true;
23306                 this.selectPrev();
23307             },
23308
23309             "down" : function(e){
23310                 if(!this.isExpanded()){
23311                     this.onTriggerClick();
23312                 }else{
23313                     this.inKeyMode = true;
23314                     this.selectNext();
23315                 }
23316             },
23317
23318             "enter" : function(e){
23319                 this.onViewClick();
23320                 //return true;
23321             },
23322
23323             "esc" : function(e){
23324                 this.collapse();
23325             },
23326
23327             "tab" : function(e){
23328                 this.onViewClick(false);
23329                 this.fireEvent("specialkey", this, e);
23330                 return true;
23331             },
23332
23333             scope : this,
23334
23335             doRelay : function(foo, bar, hname){
23336                 if(hname == 'down' || this.scope.isExpanded()){
23337                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23338                 }
23339                 return true;
23340             },
23341
23342             forceKeyDown: true
23343         });
23344         this.queryDelay = Math.max(this.queryDelay || 10,
23345                 this.mode == 'local' ? 10 : 250);
23346         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23347         if(this.typeAhead){
23348             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23349         }
23350         if(this.editable !== false){
23351             this.el.on("keyup", this.onKeyUp, this);
23352         }
23353         if(this.forceSelection){
23354             this.on('blur', this.doForce, this);
23355         }
23356     },
23357
23358     onDestroy : function(){
23359         if(this.view){
23360             this.view.setStore(null);
23361             this.view.el.removeAllListeners();
23362             this.view.el.remove();
23363             this.view.purgeListeners();
23364         }
23365         if(this.list){
23366             this.list.destroy();
23367         }
23368         if(this.store){
23369             this.store.un('beforeload', this.onBeforeLoad, this);
23370             this.store.un('load', this.onLoad, this);
23371             this.store.un('loadexception', this.onLoadException, this);
23372         }
23373         Roo.form.ComboBox.superclass.onDestroy.call(this);
23374     },
23375
23376     // private
23377     fireKey : function(e){
23378         if(e.isNavKeyPress() && !this.list.isVisible()){
23379             this.fireEvent("specialkey", this, e);
23380         }
23381     },
23382
23383     // private
23384     onResize: function(w, h){
23385         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23386         
23387         if(typeof w != 'number'){
23388             // we do not handle it!?!?
23389             return;
23390         }
23391         var tw = this.trigger.getWidth();
23392         tw += this.addicon ? this.addicon.getWidth() : 0;
23393         tw += this.editicon ? this.editicon.getWidth() : 0;
23394         var x = w - tw;
23395         this.el.setWidth( this.adjustWidth('input', x));
23396             
23397         this.trigger.setStyle('left', x+'px');
23398         
23399         if(this.list && this.listWidth === undefined){
23400             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23401             this.list.setWidth(lw);
23402             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23403         }
23404         
23405     
23406         
23407     },
23408
23409     /**
23410      * Allow or prevent the user from directly editing the field text.  If false is passed,
23411      * the user will only be able to select from the items defined in the dropdown list.  This method
23412      * is the runtime equivalent of setting the 'editable' config option at config time.
23413      * @param {Boolean} value True to allow the user to directly edit the field text
23414      */
23415     setEditable : function(value){
23416         if(value == this.editable){
23417             return;
23418         }
23419         this.editable = value;
23420         if(!value){
23421             this.el.dom.setAttribute('readOnly', true);
23422             this.el.on('mousedown', this.onTriggerClick,  this);
23423             this.el.addClass('x-combo-noedit');
23424         }else{
23425             this.el.dom.setAttribute('readOnly', false);
23426             this.el.un('mousedown', this.onTriggerClick,  this);
23427             this.el.removeClass('x-combo-noedit');
23428         }
23429     },
23430
23431     // private
23432     onBeforeLoad : function(){
23433         if(!this.hasFocus){
23434             return;
23435         }
23436         this.innerList.update(this.loadingText ?
23437                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23438         this.restrictHeight();
23439         this.selectedIndex = -1;
23440     },
23441
23442     // private
23443     onLoad : function(){
23444         if(!this.hasFocus){
23445             return;
23446         }
23447         if(this.store.getCount() > 0){
23448             this.expand();
23449             this.restrictHeight();
23450             if(this.lastQuery == this.allQuery){
23451                 if(this.editable){
23452                     this.el.dom.select();
23453                 }
23454                 if(!this.selectByValue(this.value, true)){
23455                     this.select(0, true);
23456                 }
23457             }else{
23458                 this.selectNext();
23459                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23460                     this.taTask.delay(this.typeAheadDelay);
23461                 }
23462             }
23463         }else{
23464             this.onEmptyResults();
23465         }
23466         //this.el.focus();
23467     },
23468     // private
23469     onLoadException : function()
23470     {
23471         this.collapse();
23472         Roo.log(this.store.reader.jsonData);
23473         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23474             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23475         }
23476         
23477         
23478     },
23479     // private
23480     onTypeAhead : function(){
23481         if(this.store.getCount() > 0){
23482             var r = this.store.getAt(0);
23483             var newValue = r.data[this.displayField];
23484             var len = newValue.length;
23485             var selStart = this.getRawValue().length;
23486             if(selStart != len){
23487                 this.setRawValue(newValue);
23488                 this.selectText(selStart, newValue.length);
23489             }
23490         }
23491     },
23492
23493     // private
23494     onSelect : function(record, index){
23495         if(this.fireEvent('beforeselect', this, record, index) !== false){
23496             this.setFromData(index > -1 ? record.data : false);
23497             this.collapse();
23498             this.fireEvent('select', this, record, index);
23499         }
23500     },
23501
23502     /**
23503      * Returns the currently selected field value or empty string if no value is set.
23504      * @return {String} value The selected value
23505      */
23506     getValue : function(){
23507         if(this.valueField){
23508             return typeof this.value != 'undefined' ? this.value : '';
23509         }else{
23510             return Roo.form.ComboBox.superclass.getValue.call(this);
23511         }
23512     },
23513
23514     /**
23515      * Clears any text/value currently set in the field
23516      */
23517     clearValue : function(){
23518         if(this.hiddenField){
23519             this.hiddenField.value = '';
23520         }
23521         this.value = '';
23522         this.setRawValue('');
23523         this.lastSelectionText = '';
23524         this.applyEmptyText();
23525     },
23526
23527     /**
23528      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23529      * will be displayed in the field.  If the value does not match the data value of an existing item,
23530      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23531      * Otherwise the field will be blank (although the value will still be set).
23532      * @param {String} value The value to match
23533      */
23534     setValue : function(v){
23535         var text = v;
23536         if(this.valueField){
23537             var r = this.findRecord(this.valueField, v);
23538             if(r){
23539                 text = r.data[this.displayField];
23540             }else if(this.valueNotFoundText !== undefined){
23541                 text = this.valueNotFoundText;
23542             }
23543         }
23544         this.lastSelectionText = text;
23545         if(this.hiddenField){
23546             this.hiddenField.value = v;
23547         }
23548         Roo.form.ComboBox.superclass.setValue.call(this, text);
23549         this.value = v;
23550     },
23551     /**
23552      * @property {Object} the last set data for the element
23553      */
23554     
23555     lastData : false,
23556     /**
23557      * Sets the value of the field based on a object which is related to the record format for the store.
23558      * @param {Object} value the value to set as. or false on reset?
23559      */
23560     setFromData : function(o){
23561         var dv = ''; // display value
23562         var vv = ''; // value value..
23563         this.lastData = o;
23564         if (this.displayField) {
23565             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23566         } else {
23567             // this is an error condition!!!
23568             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23569         }
23570         
23571         if(this.valueField){
23572             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23573         }
23574         if(this.hiddenField){
23575             this.hiddenField.value = vv;
23576             
23577             this.lastSelectionText = dv;
23578             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23579             this.value = vv;
23580             return;
23581         }
23582         // no hidden field.. - we store the value in 'value', but still display
23583         // display field!!!!
23584         this.lastSelectionText = dv;
23585         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23586         this.value = vv;
23587         
23588         
23589     },
23590     // private
23591     reset : function(){
23592         // overridden so that last data is reset..
23593         this.setValue(this.originalValue);
23594         this.clearInvalid();
23595         this.lastData = false;
23596     },
23597     // private
23598     findRecord : function(prop, value){
23599         var record;
23600         if(this.store.getCount() > 0){
23601             this.store.each(function(r){
23602                 if(r.data[prop] == value){
23603                     record = r;
23604                     return false;
23605                 }
23606                 return true;
23607             });
23608         }
23609         return record;
23610     },
23611     
23612     getName: function()
23613     {
23614         // returns hidden if it's set..
23615         if (!this.rendered) {return ''};
23616         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23617         
23618     },
23619     // private
23620     onViewMove : function(e, t){
23621         this.inKeyMode = false;
23622     },
23623
23624     // private
23625     onViewOver : function(e, t){
23626         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23627             return;
23628         }
23629         var item = this.view.findItemFromChild(t);
23630         if(item){
23631             var index = this.view.indexOf(item);
23632             this.select(index, false);
23633         }
23634     },
23635
23636     // private
23637     onViewClick : function(doFocus)
23638     {
23639         var index = this.view.getSelectedIndexes()[0];
23640         var r = this.store.getAt(index);
23641         if(r){
23642             this.onSelect(r, index);
23643         }
23644         if(doFocus !== false && !this.blockFocus){
23645             this.el.focus();
23646         }
23647     },
23648
23649     // private
23650     restrictHeight : function(){
23651         this.innerList.dom.style.height = '';
23652         var inner = this.innerList.dom;
23653         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23654         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23655         this.list.beginUpdate();
23656         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23657         this.list.alignTo(this.el, this.listAlign);
23658         this.list.endUpdate();
23659     },
23660
23661     // private
23662     onEmptyResults : function(){
23663         this.collapse();
23664     },
23665
23666     /**
23667      * Returns true if the dropdown list is expanded, else false.
23668      */
23669     isExpanded : function(){
23670         return this.list.isVisible();
23671     },
23672
23673     /**
23674      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23675      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23676      * @param {String} value The data value of the item to select
23677      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23678      * selected item if it is not currently in view (defaults to true)
23679      * @return {Boolean} True if the value matched an item in the list, else false
23680      */
23681     selectByValue : function(v, scrollIntoView){
23682         if(v !== undefined && v !== null){
23683             var r = this.findRecord(this.valueField || this.displayField, v);
23684             if(r){
23685                 this.select(this.store.indexOf(r), scrollIntoView);
23686                 return true;
23687             }
23688         }
23689         return false;
23690     },
23691
23692     /**
23693      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23694      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23695      * @param {Number} index The zero-based index of the list item to select
23696      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23697      * selected item if it is not currently in view (defaults to true)
23698      */
23699     select : function(index, scrollIntoView){
23700         this.selectedIndex = index;
23701         this.view.select(index);
23702         if(scrollIntoView !== false){
23703             var el = this.view.getNode(index);
23704             if(el){
23705                 this.innerList.scrollChildIntoView(el, false);
23706             }
23707         }
23708     },
23709
23710     // private
23711     selectNext : function(){
23712         var ct = this.store.getCount();
23713         if(ct > 0){
23714             if(this.selectedIndex == -1){
23715                 this.select(0);
23716             }else if(this.selectedIndex < ct-1){
23717                 this.select(this.selectedIndex+1);
23718             }
23719         }
23720     },
23721
23722     // private
23723     selectPrev : function(){
23724         var ct = this.store.getCount();
23725         if(ct > 0){
23726             if(this.selectedIndex == -1){
23727                 this.select(0);
23728             }else if(this.selectedIndex != 0){
23729                 this.select(this.selectedIndex-1);
23730             }
23731         }
23732     },
23733
23734     // private
23735     onKeyUp : function(e){
23736         if(this.editable !== false && !e.isSpecialKey()){
23737             this.lastKey = e.getKey();
23738             this.dqTask.delay(this.queryDelay);
23739         }
23740     },
23741
23742     // private
23743     validateBlur : function(){
23744         return !this.list || !this.list.isVisible();   
23745     },
23746
23747     // private
23748     initQuery : function(){
23749         this.doQuery(this.getRawValue());
23750     },
23751
23752     // private
23753     doForce : function(){
23754         if(this.el.dom.value.length > 0){
23755             this.el.dom.value =
23756                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23757             this.applyEmptyText();
23758         }
23759     },
23760
23761     /**
23762      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23763      * query allowing the query action to be canceled if needed.
23764      * @param {String} query The SQL query to execute
23765      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23766      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23767      * saved in the current store (defaults to false)
23768      */
23769     doQuery : function(q, forceAll){
23770         if(q === undefined || q === null){
23771             q = '';
23772         }
23773         var qe = {
23774             query: q,
23775             forceAll: forceAll,
23776             combo: this,
23777             cancel:false
23778         };
23779         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23780             return false;
23781         }
23782         q = qe.query;
23783         forceAll = qe.forceAll;
23784         if(forceAll === true || (q.length >= this.minChars)){
23785             if(this.lastQuery != q || this.alwaysQuery){
23786                 this.lastQuery = q;
23787                 if(this.mode == 'local'){
23788                     this.selectedIndex = -1;
23789                     if(forceAll){
23790                         this.store.clearFilter();
23791                     }else{
23792                         this.store.filter(this.displayField, q);
23793                     }
23794                     this.onLoad();
23795                 }else{
23796                     this.store.baseParams[this.queryParam] = q;
23797                     this.store.load({
23798                         params: this.getParams(q)
23799                     });
23800                     this.expand();
23801                 }
23802             }else{
23803                 this.selectedIndex = -1;
23804                 this.onLoad();   
23805             }
23806         }
23807     },
23808
23809     // private
23810     getParams : function(q){
23811         var p = {};
23812         //p[this.queryParam] = q;
23813         if(this.pageSize){
23814             p.start = 0;
23815             p.limit = this.pageSize;
23816         }
23817         return p;
23818     },
23819
23820     /**
23821      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23822      */
23823     collapse : function(){
23824         if(!this.isExpanded()){
23825             return;
23826         }
23827         this.list.hide();
23828         Roo.get(document).un('mousedown', this.collapseIf, this);
23829         Roo.get(document).un('mousewheel', this.collapseIf, this);
23830         if (!this.editable) {
23831             Roo.get(document).un('keydown', this.listKeyPress, this);
23832         }
23833         this.fireEvent('collapse', this);
23834     },
23835
23836     // private
23837     collapseIf : function(e){
23838         if(!e.within(this.wrap) && !e.within(this.list)){
23839             this.collapse();
23840         }
23841     },
23842
23843     /**
23844      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23845      */
23846     expand : function(){
23847         if(this.isExpanded() || !this.hasFocus){
23848             return;
23849         }
23850         this.list.alignTo(this.el, this.listAlign);
23851         this.list.show();
23852         Roo.get(document).on('mousedown', this.collapseIf, this);
23853         Roo.get(document).on('mousewheel', this.collapseIf, this);
23854         if (!this.editable) {
23855             Roo.get(document).on('keydown', this.listKeyPress, this);
23856         }
23857         
23858         this.fireEvent('expand', this);
23859     },
23860
23861     // private
23862     // Implements the default empty TriggerField.onTriggerClick function
23863     onTriggerClick : function(){
23864         if(this.disabled){
23865             return;
23866         }
23867         if(this.isExpanded()){
23868             this.collapse();
23869             if (!this.blockFocus) {
23870                 this.el.focus();
23871             }
23872             
23873         }else {
23874             this.hasFocus = true;
23875             if(this.triggerAction == 'all') {
23876                 this.doQuery(this.allQuery, true);
23877             } else {
23878                 this.doQuery(this.getRawValue());
23879             }
23880             if (!this.blockFocus) {
23881                 this.el.focus();
23882             }
23883         }
23884     },
23885     listKeyPress : function(e)
23886     {
23887         //Roo.log('listkeypress');
23888         // scroll to first matching element based on key pres..
23889         if (e.isSpecialKey()) {
23890             return false;
23891         }
23892         var k = String.fromCharCode(e.getKey()).toUpperCase();
23893         //Roo.log(k);
23894         var match  = false;
23895         var csel = this.view.getSelectedNodes();
23896         var cselitem = false;
23897         if (csel.length) {
23898             var ix = this.view.indexOf(csel[0]);
23899             cselitem  = this.store.getAt(ix);
23900             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23901                 cselitem = false;
23902             }
23903             
23904         }
23905         
23906         this.store.each(function(v) { 
23907             if (cselitem) {
23908                 // start at existing selection.
23909                 if (cselitem.id == v.id) {
23910                     cselitem = false;
23911                 }
23912                 return;
23913             }
23914                 
23915             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23916                 match = this.store.indexOf(v);
23917                 return false;
23918             }
23919         }, this);
23920         
23921         if (match === false) {
23922             return true; // no more action?
23923         }
23924         // scroll to?
23925         this.view.select(match);
23926         var sn = Roo.get(this.view.getSelectedNodes()[0])
23927         sn.scrollIntoView(sn.dom.parentNode, false);
23928     }
23929
23930     /** 
23931     * @cfg {Boolean} grow 
23932     * @hide 
23933     */
23934     /** 
23935     * @cfg {Number} growMin 
23936     * @hide 
23937     */
23938     /** 
23939     * @cfg {Number} growMax 
23940     * @hide 
23941     */
23942     /**
23943      * @hide
23944      * @method autoSize
23945      */
23946 });/*
23947  * Based on:
23948  * Ext JS Library 1.1.1
23949  * Copyright(c) 2006-2007, Ext JS, LLC.
23950  *
23951  * Originally Released Under LGPL - original licence link has changed is not relivant.
23952  *
23953  * Fork - LGPL
23954  * <script type="text/javascript">
23955  */
23956 /**
23957  * @class Roo.form.Checkbox
23958  * @extends Roo.form.Field
23959  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23960  * @constructor
23961  * Creates a new Checkbox
23962  * @param {Object} config Configuration options
23963  */
23964 Roo.form.Checkbox = function(config){
23965     Roo.form.Checkbox.superclass.constructor.call(this, config);
23966     this.addEvents({
23967         /**
23968          * @event check
23969          * Fires when the checkbox is checked or unchecked.
23970              * @param {Roo.form.Checkbox} this This checkbox
23971              * @param {Boolean} checked The new checked value
23972              */
23973         check : true
23974     });
23975 };
23976
23977 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23978     /**
23979      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23980      */
23981     focusClass : undefined,
23982     /**
23983      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23984      */
23985     fieldClass: "x-form-field",
23986     /**
23987      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23988      */
23989     checked: false,
23990     /**
23991      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23992      * {tag: "input", type: "checkbox", autocomplete: "off"})
23993      */
23994     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23995     /**
23996      * @cfg {String} boxLabel The text that appears beside the checkbox
23997      */
23998     boxLabel : "",
23999     /**
24000      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24001      */  
24002     inputValue : '1',
24003     /**
24004      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24005      */
24006      valueOff: '0', // value when not checked..
24007
24008     actionMode : 'viewEl', 
24009     //
24010     // private
24011     itemCls : 'x-menu-check-item x-form-item',
24012     groupClass : 'x-menu-group-item',
24013     inputType : 'hidden',
24014     
24015     
24016     inSetChecked: false, // check that we are not calling self...
24017     
24018     inputElement: false, // real input element?
24019     basedOn: false, // ????
24020     
24021     isFormField: true, // not sure where this is needed!!!!
24022
24023     onResize : function(){
24024         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24025         if(!this.boxLabel){
24026             this.el.alignTo(this.wrap, 'c-c');
24027         }
24028     },
24029
24030     initEvents : function(){
24031         Roo.form.Checkbox.superclass.initEvents.call(this);
24032         this.el.on("click", this.onClick,  this);
24033         this.el.on("change", this.onClick,  this);
24034     },
24035
24036
24037     getResizeEl : function(){
24038         return this.wrap;
24039     },
24040
24041     getPositionEl : function(){
24042         return this.wrap;
24043     },
24044
24045     // private
24046     onRender : function(ct, position){
24047         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24048         /*
24049         if(this.inputValue !== undefined){
24050             this.el.dom.value = this.inputValue;
24051         }
24052         */
24053         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24054         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24055         var viewEl = this.wrap.createChild({ 
24056             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24057         this.viewEl = viewEl;   
24058         this.wrap.on('click', this.onClick,  this); 
24059         
24060         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24061         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24062         
24063         
24064         
24065         if(this.boxLabel){
24066             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24067         //    viewEl.on('click', this.onClick,  this); 
24068         }
24069         //if(this.checked){
24070             this.setChecked(this.checked);
24071         //}else{
24072             //this.checked = this.el.dom;
24073         //}
24074
24075     },
24076
24077     // private
24078     initValue : Roo.emptyFn,
24079
24080     /**
24081      * Returns the checked state of the checkbox.
24082      * @return {Boolean} True if checked, else false
24083      */
24084     getValue : function(){
24085         if(this.el){
24086             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24087         }
24088         return this.valueOff;
24089         
24090     },
24091
24092         // private
24093     onClick : function(){ 
24094         this.setChecked(!this.checked);
24095
24096         //if(this.el.dom.checked != this.checked){
24097         //    this.setValue(this.el.dom.checked);
24098        // }
24099     },
24100
24101     /**
24102      * Sets the checked state of the checkbox.
24103      * On is always based on a string comparison between inputValue and the param.
24104      * @param {Boolean/String} value - the value to set 
24105      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24106      */
24107     setValue : function(v,suppressEvent){
24108         
24109         
24110         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24111         //if(this.el && this.el.dom){
24112         //    this.el.dom.checked = this.checked;
24113         //    this.el.dom.defaultChecked = this.checked;
24114         //}
24115         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24116         //this.fireEvent("check", this, this.checked);
24117     },
24118     // private..
24119     setChecked : function(state,suppressEvent)
24120     {
24121         if (this.inSetChecked) {
24122             this.checked = state;
24123             return;
24124         }
24125         
24126     
24127         if(this.wrap){
24128             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24129         }
24130         this.checked = state;
24131         if(suppressEvent !== true){
24132             this.fireEvent('check', this, state);
24133         }
24134         this.inSetChecked = true;
24135         this.el.dom.value = state ? this.inputValue : this.valueOff;
24136         this.inSetChecked = false;
24137         
24138     },
24139     // handle setting of hidden value by some other method!!?!?
24140     setFromHidden: function()
24141     {
24142         if(!this.el){
24143             return;
24144         }
24145         //console.log("SET FROM HIDDEN");
24146         //alert('setFrom hidden');
24147         this.setValue(this.el.dom.value);
24148     },
24149     
24150     onDestroy : function()
24151     {
24152         if(this.viewEl){
24153             Roo.get(this.viewEl).remove();
24154         }
24155          
24156         Roo.form.Checkbox.superclass.onDestroy.call(this);
24157     }
24158
24159 });/*
24160  * Based on:
24161  * Ext JS Library 1.1.1
24162  * Copyright(c) 2006-2007, Ext JS, LLC.
24163  *
24164  * Originally Released Under LGPL - original licence link has changed is not relivant.
24165  *
24166  * Fork - LGPL
24167  * <script type="text/javascript">
24168  */
24169  
24170 /**
24171  * @class Roo.form.Radio
24172  * @extends Roo.form.Checkbox
24173  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24174  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24175  * @constructor
24176  * Creates a new Radio
24177  * @param {Object} config Configuration options
24178  */
24179 Roo.form.Radio = function(){
24180     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24181 };
24182 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24183     inputType: 'radio',
24184
24185     /**
24186      * If this radio is part of a group, it will return the selected value
24187      * @return {String}
24188      */
24189     getGroupValue : function(){
24190         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24191     }
24192 });//<script type="text/javascript">
24193
24194 /*
24195  * Ext JS Library 1.1.1
24196  * Copyright(c) 2006-2007, Ext JS, LLC.
24197  * licensing@extjs.com
24198  * 
24199  * http://www.extjs.com/license
24200  */
24201  
24202  /*
24203   * 
24204   * Known bugs:
24205   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
24206   * - IE ? - no idea how much works there.
24207   * 
24208   * 
24209   * 
24210   */
24211  
24212
24213 /**
24214  * @class Ext.form.HtmlEditor
24215  * @extends Ext.form.Field
24216  * Provides a lightweight HTML Editor component.
24217  *
24218  * This has been tested on Fireforx / Chrome.. IE may not be so great..
24219  * 
24220  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
24221  * supported by this editor.</b><br/><br/>
24222  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
24223  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24224  */
24225 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
24226       /**
24227      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24228      */
24229     toolbars : false,
24230     /**
24231      * @cfg {String} createLinkText The default text for the create link prompt
24232      */
24233     createLinkText : 'Please enter the URL for the link:',
24234     /**
24235      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
24236      */
24237     defaultLinkValue : 'http:/'+'/',
24238    
24239      /**
24240      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24241      *                        Roo.resizable.
24242      */
24243     resizable : false,
24244      /**
24245      * @cfg {Number} height (in pixels)
24246      */   
24247     height: 300,
24248    /**
24249      * @cfg {Number} width (in pixels)
24250      */   
24251     width: 500,
24252     
24253     /**
24254      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24255      * 
24256      */
24257     stylesheets: false,
24258     
24259     // id of frame..
24260     frameId: false,
24261     
24262     // private properties
24263     validationEvent : false,
24264     deferHeight: true,
24265     initialized : false,
24266     activated : false,
24267     sourceEditMode : false,
24268     onFocus : Roo.emptyFn,
24269     iframePad:3,
24270     hideMode:'offsets',
24271     
24272     defaultAutoCreate : { // modified by initCompnoent..
24273         tag: "textarea",
24274         style:"width:500px;height:300px;",
24275         autocomplete: "off"
24276     },
24277
24278     // private
24279     initComponent : function(){
24280         this.addEvents({
24281             /**
24282              * @event initialize
24283              * Fires when the editor is fully initialized (including the iframe)
24284              * @param {HtmlEditor} this
24285              */
24286             initialize: true,
24287             /**
24288              * @event activate
24289              * Fires when the editor is first receives the focus. Any insertion must wait
24290              * until after this event.
24291              * @param {HtmlEditor} this
24292              */
24293             activate: true,
24294              /**
24295              * @event beforesync
24296              * Fires before the textarea is updated with content from the editor iframe. Return false
24297              * to cancel the sync.
24298              * @param {HtmlEditor} this
24299              * @param {String} html
24300              */
24301             beforesync: true,
24302              /**
24303              * @event beforepush
24304              * Fires before the iframe editor is updated with content from the textarea. Return false
24305              * to cancel the push.
24306              * @param {HtmlEditor} this
24307              * @param {String} html
24308              */
24309             beforepush: true,
24310              /**
24311              * @event sync
24312              * Fires when the textarea is updated with content from the editor iframe.
24313              * @param {HtmlEditor} this
24314              * @param {String} html
24315              */
24316             sync: true,
24317              /**
24318              * @event push
24319              * Fires when the iframe editor is updated with content from the textarea.
24320              * @param {HtmlEditor} this
24321              * @param {String} html
24322              */
24323             push: true,
24324              /**
24325              * @event editmodechange
24326              * Fires when the editor switches edit modes
24327              * @param {HtmlEditor} this
24328              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24329              */
24330             editmodechange: true,
24331             /**
24332              * @event editorevent
24333              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24334              * @param {HtmlEditor} this
24335              */
24336             editorevent: true
24337         });
24338         this.defaultAutoCreate =  {
24339             tag: "textarea",
24340             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
24341             autocomplete: "off"
24342         };
24343     },
24344
24345     /**
24346      * Protected method that will not generally be called directly. It
24347      * is called when the editor creates its toolbar. Override this method if you need to
24348      * add custom toolbar buttons.
24349      * @param {HtmlEditor} editor
24350      */
24351     createToolbar : function(editor){
24352         if (!editor.toolbars || !editor.toolbars.length) {
24353             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24354         }
24355         
24356         for (var i =0 ; i < editor.toolbars.length;i++) {
24357             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24358             editor.toolbars[i].init(editor);
24359         }
24360          
24361         
24362     },
24363
24364     /**
24365      * Protected method that will not generally be called directly. It
24366      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24367      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24368      */
24369     getDocMarkup : function(){
24370         // body styles..
24371         var st = '';
24372         if (this.stylesheets === false) {
24373             
24374             Roo.get(document.head).select('style').each(function(node) {
24375                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24376             });
24377             
24378             Roo.get(document.head).select('link').each(function(node) { 
24379                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24380             });
24381             
24382         } else if (!this.stylesheets.length) {
24383                 // simple..
24384                 st = '<style type="text/css">' +
24385                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24386                    '</style>';
24387         } else {
24388             Roo.each(this.stylesheets, function(s) {
24389                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24390             });
24391             
24392         }
24393         
24394         st +=  '<style type="text/css">' +
24395             'IMG { cursor: pointer } ' +
24396         '</style>';
24397
24398         
24399         return '<html><head>' + st  +
24400             //<style type="text/css">' +
24401             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24402             //'</style>' +
24403             ' </head><body class="roo-htmleditor-body"></body></html>';
24404     },
24405
24406     // private
24407     onRender : function(ct, position)
24408     {
24409         var _t = this;
24410         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24411         this.el.dom.style.border = '0 none';
24412         this.el.dom.setAttribute('tabIndex', -1);
24413         this.el.addClass('x-hidden');
24414         if(Roo.isIE){ // fix IE 1px bogus margin
24415             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24416         }
24417         this.wrap = this.el.wrap({
24418             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24419         });
24420         
24421         if (this.resizable) {
24422             this.resizeEl = new Roo.Resizable(this.wrap, {
24423                 pinned : true,
24424                 wrap: true,
24425                 dynamic : true,
24426                 minHeight : this.height,
24427                 height: this.height,
24428                 handles : this.resizable,
24429                 width: this.width,
24430                 listeners : {
24431                     resize : function(r, w, h) {
24432                         _t.onResize(w,h); // -something
24433                     }
24434                 }
24435             });
24436             
24437         }
24438
24439         this.frameId = Roo.id();
24440         
24441         this.createToolbar(this);
24442         
24443       
24444         
24445         var iframe = this.wrap.createChild({
24446             tag: 'iframe',
24447             id: this.frameId,
24448             name: this.frameId,
24449             frameBorder : 'no',
24450             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24451         }, this.el
24452         );
24453         
24454        // console.log(iframe);
24455         //this.wrap.dom.appendChild(iframe);
24456
24457         this.iframe = iframe.dom;
24458
24459          this.assignDocWin();
24460         
24461         this.doc.designMode = 'on';
24462        
24463         this.doc.open();
24464         this.doc.write(this.getDocMarkup());
24465         this.doc.close();
24466
24467         
24468         var task = { // must defer to wait for browser to be ready
24469             run : function(){
24470                 //console.log("run task?" + this.doc.readyState);
24471                 this.assignDocWin();
24472                 if(this.doc.body || this.doc.readyState == 'complete'){
24473                     try {
24474                         this.doc.designMode="on";
24475                     } catch (e) {
24476                         return;
24477                     }
24478                     Roo.TaskMgr.stop(task);
24479                     this.initEditor.defer(10, this);
24480                 }
24481             },
24482             interval : 10,
24483             duration:10000,
24484             scope: this
24485         };
24486         Roo.TaskMgr.start(task);
24487
24488         if(!this.width){
24489             this.setSize(this.wrap.getSize());
24490         }
24491         if (this.resizeEl) {
24492             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24493             // should trigger onReize..
24494         }
24495     },
24496
24497     // private
24498     onResize : function(w, h)
24499     {
24500         //Roo.log('resize: ' +w + ',' + h );
24501         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24502         if(this.el && this.iframe){
24503             if(typeof w == 'number'){
24504                 var aw = w - this.wrap.getFrameWidth('lr');
24505                 this.el.setWidth(this.adjustWidth('textarea', aw));
24506                 this.iframe.style.width = aw + 'px';
24507             }
24508             if(typeof h == 'number'){
24509                 var tbh = 0;
24510                 for (var i =0; i < this.toolbars.length;i++) {
24511                     // fixme - ask toolbars for heights?
24512                     tbh += this.toolbars[i].tb.el.getHeight();
24513                     if (this.toolbars[i].footer) {
24514                         tbh += this.toolbars[i].footer.el.getHeight();
24515                     }
24516                 }
24517                 
24518                 
24519                 
24520                 
24521                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24522                 ah -= 5; // knock a few pixes off for look..
24523                 this.el.setHeight(this.adjustWidth('textarea', ah));
24524                 this.iframe.style.height = ah + 'px';
24525                 if(this.doc){
24526                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24527                 }
24528             }
24529         }
24530     },
24531
24532     /**
24533      * Toggles the editor between standard and source edit mode.
24534      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24535      */
24536     toggleSourceEdit : function(sourceEditMode){
24537         
24538         this.sourceEditMode = sourceEditMode === true;
24539         
24540         if(this.sourceEditMode){
24541           
24542             this.syncValue();
24543             this.iframe.className = 'x-hidden';
24544             this.el.removeClass('x-hidden');
24545             this.el.dom.removeAttribute('tabIndex');
24546             this.el.focus();
24547         }else{
24548              
24549             this.pushValue();
24550             this.iframe.className = '';
24551             this.el.addClass('x-hidden');
24552             this.el.dom.setAttribute('tabIndex', -1);
24553             this.deferFocus();
24554         }
24555         this.setSize(this.wrap.getSize());
24556         this.fireEvent('editmodechange', this, this.sourceEditMode);
24557     },
24558
24559     // private used internally
24560     createLink : function(){
24561         var url = prompt(this.createLinkText, this.defaultLinkValue);
24562         if(url && url != 'http:/'+'/'){
24563             this.relayCmd('createlink', url);
24564         }
24565     },
24566
24567     // private (for BoxComponent)
24568     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24569
24570     // private (for BoxComponent)
24571     getResizeEl : function(){
24572         return this.wrap;
24573     },
24574
24575     // private (for BoxComponent)
24576     getPositionEl : function(){
24577         return this.wrap;
24578     },
24579
24580     // private
24581     initEvents : function(){
24582         this.originalValue = this.getValue();
24583     },
24584
24585     /**
24586      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24587      * @method
24588      */
24589     markInvalid : Roo.emptyFn,
24590     /**
24591      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24592      * @method
24593      */
24594     clearInvalid : Roo.emptyFn,
24595
24596     setValue : function(v){
24597         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24598         this.pushValue();
24599     },
24600
24601     /**
24602      * Protected method that will not generally be called directly. If you need/want
24603      * custom HTML cleanup, this is the method you should override.
24604      * @param {String} html The HTML to be cleaned
24605      * return {String} The cleaned HTML
24606      */
24607     cleanHtml : function(html){
24608         html = String(html);
24609         if(html.length > 5){
24610             if(Roo.isSafari){ // strip safari nonsense
24611                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24612             }
24613         }
24614         if(html == '&nbsp;'){
24615             html = '';
24616         }
24617         return html;
24618     },
24619
24620     /**
24621      * Protected method that will not generally be called directly. Syncs the contents
24622      * of the editor iframe with the textarea.
24623      */
24624     syncValue : function(){
24625         if(this.initialized){
24626             var bd = (this.doc.body || this.doc.documentElement);
24627             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24628             var html = bd.innerHTML;
24629             if(Roo.isSafari){
24630                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24631                 var m = bs.match(/text-align:(.*?);/i);
24632                 if(m && m[1]){
24633                     html = '<div style="'+m[0]+'">' + html + '</div>';
24634                 }
24635             }
24636             html = this.cleanHtml(html);
24637             // fix up the special chars..
24638             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
24639                 return "&#"+b.charCodeAt()+";" 
24640             });
24641             if(this.fireEvent('beforesync', this, html) !== false){
24642                 this.el.dom.value = html;
24643                 this.fireEvent('sync', this, html);
24644             }
24645         }
24646     },
24647
24648     /**
24649      * Protected method that will not generally be called directly. Pushes the value of the textarea
24650      * into the iframe editor.
24651      */
24652     pushValue : function(){
24653         if(this.initialized){
24654             var v = this.el.dom.value;
24655             if(v.length < 1){
24656                 v = '&#160;';
24657             }
24658             
24659             if(this.fireEvent('beforepush', this, v) !== false){
24660                 var d = (this.doc.body || this.doc.documentElement);
24661                 d.innerHTML = v;
24662                 this.cleanUpPaste();
24663                 this.el.dom.value = d.innerHTML;
24664                 this.fireEvent('push', this, v);
24665             }
24666         }
24667     },
24668
24669     // private
24670     deferFocus : function(){
24671         this.focus.defer(10, this);
24672     },
24673
24674     // doc'ed in Field
24675     focus : function(){
24676         if(this.win && !this.sourceEditMode){
24677             this.win.focus();
24678         }else{
24679             this.el.focus();
24680         }
24681     },
24682     
24683     assignDocWin: function()
24684     {
24685         var iframe = this.iframe;
24686         
24687          if(Roo.isIE){
24688             this.doc = iframe.contentWindow.document;
24689             this.win = iframe.contentWindow;
24690         } else {
24691             if (!Roo.get(this.frameId)) {
24692                 return;
24693             }
24694             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24695             this.win = Roo.get(this.frameId).dom.contentWindow;
24696         }
24697     },
24698     
24699     // private
24700     initEditor : function(){
24701         //console.log("INIT EDITOR");
24702         this.assignDocWin();
24703         
24704         
24705         
24706         this.doc.designMode="on";
24707         this.doc.open();
24708         this.doc.write(this.getDocMarkup());
24709         this.doc.close();
24710         
24711         var dbody = (this.doc.body || this.doc.documentElement);
24712         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24713         // this copies styles from the containing element into thsi one..
24714         // not sure why we need all of this..
24715         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24716         ss['background-attachment'] = 'fixed'; // w3c
24717         dbody.bgProperties = 'fixed'; // ie
24718         Roo.DomHelper.applyStyles(dbody, ss);
24719         Roo.EventManager.on(this.doc, {
24720             //'mousedown': this.onEditorEvent,
24721             'mouseup': this.onEditorEvent,
24722             'dblclick': this.onEditorEvent,
24723             'click': this.onEditorEvent,
24724             'keyup': this.onEditorEvent,
24725             buffer:100,
24726             scope: this
24727         });
24728         if(Roo.isGecko){
24729             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24730         }
24731         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24732             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24733         }
24734         this.initialized = true;
24735
24736         this.fireEvent('initialize', this);
24737         this.pushValue();
24738     },
24739
24740     // private
24741     onDestroy : function(){
24742         
24743         
24744         
24745         if(this.rendered){
24746             
24747             for (var i =0; i < this.toolbars.length;i++) {
24748                 // fixme - ask toolbars for heights?
24749                 this.toolbars[i].onDestroy();
24750             }
24751             
24752             this.wrap.dom.innerHTML = '';
24753             this.wrap.remove();
24754         }
24755     },
24756
24757     // private
24758     onFirstFocus : function(){
24759         
24760         this.assignDocWin();
24761         
24762         
24763         this.activated = true;
24764         for (var i =0; i < this.toolbars.length;i++) {
24765             this.toolbars[i].onFirstFocus();
24766         }
24767        
24768         if(Roo.isGecko){ // prevent silly gecko errors
24769             this.win.focus();
24770             var s = this.win.getSelection();
24771             if(!s.focusNode || s.focusNode.nodeType != 3){
24772                 var r = s.getRangeAt(0);
24773                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24774                 r.collapse(true);
24775                 this.deferFocus();
24776             }
24777             try{
24778                 this.execCmd('useCSS', true);
24779                 this.execCmd('styleWithCSS', false);
24780             }catch(e){}
24781         }
24782         this.fireEvent('activate', this);
24783     },
24784
24785     // private
24786     adjustFont: function(btn){
24787         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24788         //if(Roo.isSafari){ // safari
24789         //    adjust *= 2;
24790        // }
24791         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24792         if(Roo.isSafari){ // safari
24793             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24794             v =  (v < 10) ? 10 : v;
24795             v =  (v > 48) ? 48 : v;
24796             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24797             
24798         }
24799         
24800         
24801         v = Math.max(1, v+adjust);
24802         
24803         this.execCmd('FontSize', v  );
24804     },
24805
24806     onEditorEvent : function(e){
24807         this.fireEvent('editorevent', this, e);
24808       //  this.updateToolbar();
24809         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24810     },
24811
24812     insertTag : function(tg)
24813     {
24814         // could be a bit smarter... -> wrap the current selected tRoo..
24815         
24816         this.execCmd("formatblock",   tg);
24817         
24818     },
24819     
24820     insertText : function(txt)
24821     {
24822         
24823         
24824         range = this.createRange();
24825         range.deleteContents();
24826                //alert(Sender.getAttribute('label'));
24827                
24828         range.insertNode(this.doc.createTextNode(txt));
24829     } ,
24830     
24831     // private
24832     relayBtnCmd : function(btn){
24833         this.relayCmd(btn.cmd);
24834     },
24835
24836     /**
24837      * Executes a Midas editor command on the editor document and performs necessary focus and
24838      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24839      * @param {String} cmd The Midas command
24840      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24841      */
24842     relayCmd : function(cmd, value){
24843         this.win.focus();
24844         this.execCmd(cmd, value);
24845         this.fireEvent('editorevent', this);
24846         //this.updateToolbar();
24847         this.deferFocus();
24848     },
24849
24850     /**
24851      * Executes a Midas editor command directly on the editor document.
24852      * For visual commands, you should use {@link #relayCmd} instead.
24853      * <b>This should only be called after the editor is initialized.</b>
24854      * @param {String} cmd The Midas command
24855      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24856      */
24857     execCmd : function(cmd, value){
24858         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24859         this.syncValue();
24860     },
24861  
24862  
24863    
24864     /**
24865      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24866      * to insert tRoo.
24867      * @param {String} text | dom node.. 
24868      */
24869     insertAtCursor : function(text)
24870     {
24871         
24872         
24873         
24874         if(!this.activated){
24875             return;
24876         }
24877         /*
24878         if(Roo.isIE){
24879             this.win.focus();
24880             var r = this.doc.selection.createRange();
24881             if(r){
24882                 r.collapse(true);
24883                 r.pasteHTML(text);
24884                 this.syncValue();
24885                 this.deferFocus();
24886             
24887             }
24888             return;
24889         }
24890         */
24891         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24892             this.win.focus();
24893             
24894             
24895             // from jquery ui (MIT licenced)
24896             var range, node;
24897             var win = this.win;
24898             
24899             if (win.getSelection && win.getSelection().getRangeAt) {
24900                 range = win.getSelection().getRangeAt(0);
24901                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24902                 range.insertNode(node);
24903             } else if (win.document.selection && win.document.selection.createRange) {
24904                 // no firefox support
24905                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24906                 win.document.selection.createRange().pasteHTML(txt);
24907             } else {
24908                 // no firefox support
24909                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24910                 this.execCmd('InsertHTML', txt);
24911             } 
24912             
24913             this.syncValue();
24914             
24915             this.deferFocus();
24916         }
24917     },
24918  // private
24919     mozKeyPress : function(e){
24920         if(e.ctrlKey){
24921             var c = e.getCharCode(), cmd;
24922           
24923             if(c > 0){
24924                 c = String.fromCharCode(c).toLowerCase();
24925                 switch(c){
24926                     case 'b':
24927                         cmd = 'bold';
24928                         break;
24929                     case 'i':
24930                         cmd = 'italic';
24931                         break;
24932                     
24933                     case 'u':
24934                         cmd = 'underline';
24935                         break;
24936                     
24937                     case 'v':
24938                         this.cleanUpPaste.defer(100, this);
24939                         return;
24940                         
24941                 }
24942                 if(cmd){
24943                     this.win.focus();
24944                     this.execCmd(cmd);
24945                     this.deferFocus();
24946                     e.preventDefault();
24947                 }
24948                 
24949             }
24950         }
24951     },
24952
24953     // private
24954     fixKeys : function(){ // load time branching for fastest keydown performance
24955         if(Roo.isIE){
24956             return function(e){
24957                 var k = e.getKey(), r;
24958                 if(k == e.TAB){
24959                     e.stopEvent();
24960                     r = this.doc.selection.createRange();
24961                     if(r){
24962                         r.collapse(true);
24963                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24964                         this.deferFocus();
24965                     }
24966                     return;
24967                 }
24968                 
24969                 if(k == e.ENTER){
24970                     r = this.doc.selection.createRange();
24971                     if(r){
24972                         var target = r.parentElement();
24973                         if(!target || target.tagName.toLowerCase() != 'li'){
24974                             e.stopEvent();
24975                             r.pasteHTML('<br />');
24976                             r.collapse(false);
24977                             r.select();
24978                         }
24979                     }
24980                 }
24981                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24982                     this.cleanUpPaste.defer(100, this);
24983                     return;
24984                 }
24985                 
24986                 
24987             };
24988         }else if(Roo.isOpera){
24989             return function(e){
24990                 var k = e.getKey();
24991                 if(k == e.TAB){
24992                     e.stopEvent();
24993                     this.win.focus();
24994                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24995                     this.deferFocus();
24996                 }
24997                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24998                     this.cleanUpPaste.defer(100, this);
24999                     return;
25000                 }
25001                 
25002             };
25003         }else if(Roo.isSafari){
25004             return function(e){
25005                 var k = e.getKey();
25006                 
25007                 if(k == e.TAB){
25008                     e.stopEvent();
25009                     this.execCmd('InsertText','\t');
25010                     this.deferFocus();
25011                     return;
25012                 }
25013                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25014                     this.cleanUpPaste.defer(100, this);
25015                     return;
25016                 }
25017                 
25018              };
25019         }
25020     }(),
25021     
25022     getAllAncestors: function()
25023     {
25024         var p = this.getSelectedNode();
25025         var a = [];
25026         if (!p) {
25027             a.push(p); // push blank onto stack..
25028             p = this.getParentElement();
25029         }
25030         
25031         
25032         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25033             a.push(p);
25034             p = p.parentNode;
25035         }
25036         a.push(this.doc.body);
25037         return a;
25038     },
25039     lastSel : false,
25040     lastSelNode : false,
25041     
25042     
25043     getSelection : function() 
25044     {
25045         this.assignDocWin();
25046         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25047     },
25048     
25049     getSelectedNode: function() 
25050     {
25051         // this may only work on Gecko!!!
25052         
25053         // should we cache this!!!!
25054         
25055         
25056         
25057          
25058         var range = this.createRange(this.getSelection()).cloneRange();
25059         
25060         if (Roo.isIE) {
25061             var parent = range.parentElement();
25062             while (true) {
25063                 var testRange = range.duplicate();
25064                 testRange.moveToElementText(parent);
25065                 if (testRange.inRange(range)) {
25066                     break;
25067                 }
25068                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25069                     break;
25070                 }
25071                 parent = parent.parentElement;
25072             }
25073             return parent;
25074         }
25075         
25076         // is ancestor a text element.
25077         var ac =  range.commonAncestorContainer;
25078         if (ac.nodeType == 3) {
25079             ac = ac.parentNode;
25080         }
25081         
25082         var ar = ac.childNodes;
25083          
25084         var nodes = [];
25085         var other_nodes = [];
25086         var has_other_nodes = false;
25087         for (var i=0;i<ar.length;i++) {
25088             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25089                 continue;
25090             }
25091             // fullly contained node.
25092             
25093             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25094                 nodes.push(ar[i]);
25095                 continue;
25096             }
25097             
25098             // probably selected..
25099             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25100                 other_nodes.push(ar[i]);
25101                 continue;
25102             }
25103             // outer..
25104             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25105                 continue;
25106             }
25107             
25108             
25109             has_other_nodes = true;
25110         }
25111         if (!nodes.length && other_nodes.length) {
25112             nodes= other_nodes;
25113         }
25114         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25115             return false;
25116         }
25117         
25118         return nodes[0];
25119     },
25120     createRange: function(sel)
25121     {
25122         // this has strange effects when using with 
25123         // top toolbar - not sure if it's a great idea.
25124         //this.editor.contentWindow.focus();
25125         if (typeof sel != "undefined") {
25126             try {
25127                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25128             } catch(e) {
25129                 return this.doc.createRange();
25130             }
25131         } else {
25132             return this.doc.createRange();
25133         }
25134     },
25135     getParentElement: function()
25136     {
25137         
25138         this.assignDocWin();
25139         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25140         
25141         var range = this.createRange(sel);
25142          
25143         try {
25144             var p = range.commonAncestorContainer;
25145             while (p.nodeType == 3) { // text node
25146                 p = p.parentNode;
25147             }
25148             return p;
25149         } catch (e) {
25150             return null;
25151         }
25152     
25153     },
25154     /***
25155      *
25156      * Range intersection.. the hard stuff...
25157      *  '-1' = before
25158      *  '0' = hits..
25159      *  '1' = after.
25160      *         [ -- selected range --- ]
25161      *   [fail]                        [fail]
25162      *
25163      *    basically..
25164      *      if end is before start or  hits it. fail.
25165      *      if start is after end or hits it fail.
25166      *
25167      *   if either hits (but other is outside. - then it's not 
25168      *   
25169      *    
25170      **/
25171     
25172     
25173     // @see http://www.thismuchiknow.co.uk/?p=64.
25174     rangeIntersectsNode : function(range, node)
25175     {
25176         var nodeRange = node.ownerDocument.createRange();
25177         try {
25178             nodeRange.selectNode(node);
25179         } catch (e) {
25180             nodeRange.selectNodeContents(node);
25181         }
25182     
25183         var rangeStartRange = range.cloneRange();
25184         rangeStartRange.collapse(true);
25185     
25186         var rangeEndRange = range.cloneRange();
25187         rangeEndRange.collapse(false);
25188     
25189         var nodeStartRange = nodeRange.cloneRange();
25190         nodeStartRange.collapse(true);
25191     
25192         var nodeEndRange = nodeRange.cloneRange();
25193         nodeEndRange.collapse(false);
25194     
25195         return rangeStartRange.compareBoundaryPoints(
25196                  Range.START_TO_START, nodeEndRange) == -1 &&
25197                rangeEndRange.compareBoundaryPoints(
25198                  Range.START_TO_START, nodeStartRange) == 1;
25199         
25200          
25201     },
25202     rangeCompareNode : function(range, node)
25203     {
25204         var nodeRange = node.ownerDocument.createRange();
25205         try {
25206             nodeRange.selectNode(node);
25207         } catch (e) {
25208             nodeRange.selectNodeContents(node);
25209         }
25210         
25211         
25212         range.collapse(true);
25213     
25214         nodeRange.collapse(true);
25215      
25216         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25217         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25218          
25219         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25220         
25221         var nodeIsBefore   =  ss == 1;
25222         var nodeIsAfter    = ee == -1;
25223         
25224         if (nodeIsBefore && nodeIsAfter)
25225             return 0; // outer
25226         if (!nodeIsBefore && nodeIsAfter)
25227             return 1; //right trailed.
25228         
25229         if (nodeIsBefore && !nodeIsAfter)
25230             return 2;  // left trailed.
25231         // fully contined.
25232         return 3;
25233     },
25234
25235     // private? - in a new class?
25236     cleanUpPaste :  function()
25237     {
25238         // cleans up the whole document..
25239          Roo.log('cleanuppaste');
25240         this.cleanUpChildren(this.doc.body);
25241         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25242         if (clean != this.doc.body.innerHTML) {
25243             this.doc.body.innerHTML = clean;
25244         }
25245         
25246     },
25247     
25248     cleanWordChars : function(input) {
25249         var he = Roo.form.HtmlEditor;
25250     
25251         var output = input;
25252         Roo.each(he.swapCodes, function(sw) { 
25253         
25254             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25255             output = output.replace(swapper, sw[1]);
25256         });
25257         return output;
25258     },
25259     
25260     
25261     cleanUpChildren : function (n)
25262     {
25263         if (!n.childNodes.length) {
25264             return;
25265         }
25266         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25267            this.cleanUpChild(n.childNodes[i]);
25268         }
25269     },
25270     
25271     
25272         
25273     
25274     cleanUpChild : function (node)
25275     {
25276         //console.log(node);
25277         if (node.nodeName == "#text") {
25278             // clean up silly Windows -- stuff?
25279             return; 
25280         }
25281         if (node.nodeName == "#comment") {
25282             node.parentNode.removeChild(node);
25283             // clean up silly Windows -- stuff?
25284             return; 
25285         }
25286         
25287         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
25288             // remove node.
25289             node.parentNode.removeChild(node);
25290             return;
25291             
25292         }
25293         
25294         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
25295         
25296         // remove <a name=....> as rendering on yahoo mailer is bored with this.
25297         
25298         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25299             remove_keep_children = true;
25300         }
25301         
25302         if (remove_keep_children) {
25303             this.cleanUpChildren(node);
25304             // inserts everything just before this node...
25305             while (node.childNodes.length) {
25306                 var cn = node.childNodes[0];
25307                 node.removeChild(cn);
25308                 node.parentNode.insertBefore(cn, node);
25309             }
25310             node.parentNode.removeChild(node);
25311             return;
25312         }
25313         
25314         if (!node.attributes || !node.attributes.length) {
25315             this.cleanUpChildren(node);
25316             return;
25317         }
25318         
25319         function cleanAttr(n,v)
25320         {
25321             
25322             if (v.match(/^\./) || v.match(/^\//)) {
25323                 return;
25324             }
25325             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25326                 return;
25327             }
25328             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
25329             node.removeAttribute(n);
25330             
25331         }
25332         
25333         function cleanStyle(n,v)
25334         {
25335             if (v.match(/expression/)) { //XSS?? should we even bother..
25336                 node.removeAttribute(n);
25337                 return;
25338             }
25339             
25340             
25341             var parts = v.split(/;/);
25342             Roo.each(parts, function(p) {
25343                 p = p.replace(/\s+/g,'');
25344                 if (!p.length) {
25345                     return true;
25346                 }
25347                 var l = p.split(':').shift().replace(/\s+/g,'');
25348                 
25349                 // only allow 'c whitelisted system attributes'
25350                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
25351                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
25352                     node.removeAttribute(n);
25353                     return false;
25354                 }
25355                 return true;
25356             });
25357             
25358             
25359         }
25360         
25361         
25362         for (var i = node.attributes.length-1; i > -1 ; i--) {
25363             var a = node.attributes[i];
25364             //console.log(a);
25365             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
25366                 node.removeAttribute(a.name);
25367                 return;
25368             }
25369             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
25370                 cleanAttr(a.name,a.value); // fixme..
25371                 return;
25372             }
25373             if (a.name == 'style') {
25374                 cleanStyle(a.name,a.value);
25375             }
25376             /// clean up MS crap..
25377             // tecnically this should be a list of valid class'es..
25378             
25379             
25380             if (a.name == 'class') {
25381                 if (a.value.match(/^Mso/)) {
25382                     node.className = '';
25383                 }
25384                 
25385                 if (a.value.match(/body/)) {
25386                     node.className = '';
25387                 }
25388             }
25389             
25390             // style cleanup!?
25391             // class cleanup?
25392             
25393         }
25394         
25395         
25396         this.cleanUpChildren(node);
25397         
25398         
25399     }
25400     
25401     
25402     // hide stuff that is not compatible
25403     /**
25404      * @event blur
25405      * @hide
25406      */
25407     /**
25408      * @event change
25409      * @hide
25410      */
25411     /**
25412      * @event focus
25413      * @hide
25414      */
25415     /**
25416      * @event specialkey
25417      * @hide
25418      */
25419     /**
25420      * @cfg {String} fieldClass @hide
25421      */
25422     /**
25423      * @cfg {String} focusClass @hide
25424      */
25425     /**
25426      * @cfg {String} autoCreate @hide
25427      */
25428     /**
25429      * @cfg {String} inputType @hide
25430      */
25431     /**
25432      * @cfg {String} invalidClass @hide
25433      */
25434     /**
25435      * @cfg {String} invalidText @hide
25436      */
25437     /**
25438      * @cfg {String} msgFx @hide
25439      */
25440     /**
25441      * @cfg {String} validateOnBlur @hide
25442      */
25443 });
25444
25445 Roo.form.HtmlEditor.white = [
25446         'area', 'br', 'img', 'input', 'hr', 'wbr',
25447         
25448        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25449        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25450        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25451        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25452        'table',   'ul',         'xmp', 
25453        
25454        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25455       'thead',   'tr', 
25456      
25457       'dir', 'menu', 'ol', 'ul', 'dl',
25458        
25459       'embed',  'object'
25460 ];
25461
25462
25463 Roo.form.HtmlEditor.black = [
25464     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25465         'applet', // 
25466         'base',   'basefont', 'bgsound', 'blink',  'body', 
25467         'frame',  'frameset', 'head',    'html',   'ilayer', 
25468         'iframe', 'layer',  'link',     'meta',    'object',   
25469         'script', 'style' ,'title',  'xml' // clean later..
25470 ];
25471 Roo.form.HtmlEditor.clean = [
25472     'script', 'style', 'title', 'xml'
25473 ];
25474 Roo.form.HtmlEditor.remove = [
25475     'font'
25476 ];
25477 // attributes..
25478
25479 Roo.form.HtmlEditor.ablack = [
25480     'on'
25481 ];
25482     
25483 Roo.form.HtmlEditor.aclean = [ 
25484     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25485 ];
25486
25487 // protocols..
25488 Roo.form.HtmlEditor.pwhite= [
25489         'http',  'https',  'mailto'
25490 ];
25491
25492 // white listed style attributes.
25493 Roo.form.HtmlEditor.cwhite= [
25494         'text-align',
25495         'font-size'
25496 ];
25497
25498
25499 Roo.form.HtmlEditor.swapCodes   =[ 
25500     [    8211, "--" ], 
25501     [    8212, "--" ], 
25502     [    8216,  "'" ],  
25503     [    8217, "'" ],  
25504     [    8220, '"' ],  
25505     [    8221, '"' ],  
25506     [    8226, "*" ],  
25507     [    8230, "..." ]
25508 ]; 
25509
25510     // <script type="text/javascript">
25511 /*
25512  * Based on
25513  * Ext JS Library 1.1.1
25514  * Copyright(c) 2006-2007, Ext JS, LLC.
25515  *  
25516  
25517  */
25518
25519 /**
25520  * @class Roo.form.HtmlEditorToolbar1
25521  * Basic Toolbar
25522  * 
25523  * Usage:
25524  *
25525  new Roo.form.HtmlEditor({
25526     ....
25527     toolbars : [
25528         new Roo.form.HtmlEditorToolbar1({
25529             disable : { fonts: 1 , format: 1, ..., ... , ...],
25530             btns : [ .... ]
25531         })
25532     }
25533      
25534  * 
25535  * @cfg {Object} disable List of elements to disable..
25536  * @cfg {Array} btns List of additional buttons.
25537  * 
25538  * 
25539  * NEEDS Extra CSS? 
25540  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25541  */
25542  
25543 Roo.form.HtmlEditor.ToolbarStandard = function(config)
25544 {
25545     
25546     Roo.apply(this, config);
25547     
25548     // default disabled, based on 'good practice'..
25549     this.disable = this.disable || {};
25550     Roo.applyIf(this.disable, {
25551         fontSize : true,
25552         colors : true,
25553         specialElements : true
25554     });
25555     
25556     
25557     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25558     // dont call parent... till later.
25559 }
25560
25561 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
25562     
25563     tb: false,
25564     
25565     rendered: false,
25566     
25567     editor : false,
25568     /**
25569      * @cfg {Object} disable  List of toolbar elements to disable
25570          
25571      */
25572     disable : false,
25573       /**
25574      * @cfg {Array} fontFamilies An array of available font families
25575      */
25576     fontFamilies : [
25577         'Arial',
25578         'Courier New',
25579         'Tahoma',
25580         'Times New Roman',
25581         'Verdana'
25582     ],
25583     
25584     specialChars : [
25585            "&#169;",
25586           "&#174;",     
25587           "&#8482;",    
25588           "&#163;" ,    
25589          // "&#8212;",    
25590           "&#8230;",    
25591           "&#247;" ,    
25592         //  "&#225;" ,     ?? a acute?
25593            "&#8364;"    , //Euro
25594        //   "&#8220;"    ,
25595         //  "&#8221;"    ,
25596         //  "&#8226;"    ,
25597           "&#176;"  //   , // degrees
25598
25599          // "&#233;"     , // e ecute
25600          // "&#250;"     , // u ecute?
25601     ],
25602     
25603     specialElements : [
25604         {
25605             text: "Insert Table",
25606             xtype: 'MenuItem',
25607             xns : Roo.Menu,
25608             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
25609                 
25610         },
25611         {    
25612             text: "Insert Image",
25613             xtype: 'MenuItem',
25614             xns : Roo.Menu,
25615             ihtml : '<img src="about:blank"/>'
25616             
25617         }
25618         
25619          
25620     ],
25621     
25622     
25623     inputElements : [ 
25624             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25625             "input:submit", "input:button", "select", "textarea", "label" ],
25626     formats : [
25627         ["p"] ,  
25628         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25629         ["pre"],[ "code"], 
25630         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25631     ],
25632      /**
25633      * @cfg {String} defaultFont default font to use.
25634      */
25635     defaultFont: 'tahoma',
25636    
25637     fontSelect : false,
25638     
25639     
25640     formatCombo : false,
25641     
25642     init : function(editor)
25643     {
25644         this.editor = editor;
25645         
25646         
25647         var fid = editor.frameId;
25648         var etb = this;
25649         function btn(id, toggle, handler){
25650             var xid = fid + '-'+ id ;
25651             return {
25652                 id : xid,
25653                 cmd : id,
25654                 cls : 'x-btn-icon x-edit-'+id,
25655                 enableToggle:toggle !== false,
25656                 scope: editor, // was editor...
25657                 handler:handler||editor.relayBtnCmd,
25658                 clickEvent:'mousedown',
25659                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25660                 tabIndex:-1
25661             };
25662         }
25663         
25664         
25665         
25666         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25667         this.tb = tb;
25668          // stop form submits
25669         tb.el.on('click', function(e){
25670             e.preventDefault(); // what does this do?
25671         });
25672
25673         if(!this.disable.font && !Roo.isSafari){
25674             /* why no safari for fonts
25675             editor.fontSelect = tb.el.createChild({
25676                 tag:'select',
25677                 tabIndex: -1,
25678                 cls:'x-font-select',
25679                 html: editor.createFontOptions()
25680             });
25681             editor.fontSelect.on('change', function(){
25682                 var font = editor.fontSelect.dom.value;
25683                 editor.relayCmd('fontname', font);
25684                 editor.deferFocus();
25685             }, editor);
25686             tb.add(
25687                 editor.fontSelect.dom,
25688                 '-'
25689             );
25690             */
25691         };
25692         if(!this.disable.formats){
25693             this.formatCombo = new Roo.form.ComboBox({
25694                 store: new Roo.data.SimpleStore({
25695                     id : 'tag',
25696                     fields: ['tag'],
25697                     data : this.formats // from states.js
25698                 }),
25699                 blockFocus : true,
25700                 //autoCreate : {tag: "div",  size: "20"},
25701                 displayField:'tag',
25702                 typeAhead: false,
25703                 mode: 'local',
25704                 editable : false,
25705                 triggerAction: 'all',
25706                 emptyText:'Add tag',
25707                 selectOnFocus:true,
25708                 width:135,
25709                 listeners : {
25710                     'select': function(c, r, i) {
25711                         editor.insertTag(r.get('tag'));
25712                         editor.focus();
25713                     }
25714                 }
25715
25716             });
25717             tb.addField(this.formatCombo);
25718             
25719         }
25720         
25721         if(!this.disable.format){
25722             tb.add(
25723                 btn('bold'),
25724                 btn('italic'),
25725                 btn('underline')
25726             );
25727         };
25728         if(!this.disable.fontSize){
25729             tb.add(
25730                 '-',
25731                 
25732                 
25733                 btn('increasefontsize', false, editor.adjustFont),
25734                 btn('decreasefontsize', false, editor.adjustFont)
25735             );
25736         };
25737         
25738         
25739         if(!this.disable.colors){
25740             tb.add(
25741                 '-', {
25742                     id:editor.frameId +'-forecolor',
25743                     cls:'x-btn-icon x-edit-forecolor',
25744                     clickEvent:'mousedown',
25745                     tooltip: this.buttonTips['forecolor'] || undefined,
25746                     tabIndex:-1,
25747                     menu : new Roo.menu.ColorMenu({
25748                         allowReselect: true,
25749                         focus: Roo.emptyFn,
25750                         value:'000000',
25751                         plain:true,
25752                         selectHandler: function(cp, color){
25753                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25754                             editor.deferFocus();
25755                         },
25756                         scope: editor,
25757                         clickEvent:'mousedown'
25758                     })
25759                 }, {
25760                     id:editor.frameId +'backcolor',
25761                     cls:'x-btn-icon x-edit-backcolor',
25762                     clickEvent:'mousedown',
25763                     tooltip: this.buttonTips['backcolor'] || undefined,
25764                     tabIndex:-1,
25765                     menu : new Roo.menu.ColorMenu({
25766                         focus: Roo.emptyFn,
25767                         value:'FFFFFF',
25768                         plain:true,
25769                         allowReselect: true,
25770                         selectHandler: function(cp, color){
25771                             if(Roo.isGecko){
25772                                 editor.execCmd('useCSS', false);
25773                                 editor.execCmd('hilitecolor', color);
25774                                 editor.execCmd('useCSS', true);
25775                                 editor.deferFocus();
25776                             }else{
25777                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25778                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25779                                 editor.deferFocus();
25780                             }
25781                         },
25782                         scope:editor,
25783                         clickEvent:'mousedown'
25784                     })
25785                 }
25786             );
25787         };
25788         // now add all the items...
25789         
25790
25791         if(!this.disable.alignments){
25792             tb.add(
25793                 '-',
25794                 btn('justifyleft'),
25795                 btn('justifycenter'),
25796                 btn('justifyright')
25797             );
25798         };
25799
25800         //if(!Roo.isSafari){
25801             if(!this.disable.links){
25802                 tb.add(
25803                     '-',
25804                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25805                 );
25806             };
25807
25808             if(!this.disable.lists){
25809                 tb.add(
25810                     '-',
25811                     btn('insertorderedlist'),
25812                     btn('insertunorderedlist')
25813                 );
25814             }
25815             if(!this.disable.sourceEdit){
25816                 tb.add(
25817                     '-',
25818                     btn('sourceedit', true, function(btn){
25819                         this.toggleSourceEdit(btn.pressed);
25820                     })
25821                 );
25822             }
25823         //}
25824         
25825         var smenu = { };
25826         // special menu.. - needs to be tidied up..
25827         if (!this.disable.special) {
25828             smenu = {
25829                 text: "&#169;",
25830                 cls: 'x-edit-none',
25831                 
25832                 menu : {
25833                     items : []
25834                 }
25835             };
25836             for (var i =0; i < this.specialChars.length; i++) {
25837                 smenu.menu.items.push({
25838                     
25839                     html: this.specialChars[i],
25840                     handler: function(a,b) {
25841                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25842                         //editor.insertAtCursor(a.html);
25843                         
25844                     },
25845                     tabIndex:-1
25846                 });
25847             }
25848             
25849             
25850             tb.add(smenu);
25851             
25852             
25853         }
25854          
25855         if (!this.disable.specialElements) {
25856             var semenu = {
25857                 text: "Other;",
25858                 cls: 'x-edit-none',
25859                 menu : {
25860                     items : []
25861                 }
25862             };
25863             for (var i =0; i < this.specialElements.length; i++) {
25864                 semenu.menu.items.push(
25865                     Roo.apply({ 
25866                         handler: function(a,b) {
25867                             editor.insertAtCursor(this.ihtml);
25868                         }
25869                     }, this.specialElements[i])
25870                 );
25871                     
25872             }
25873             
25874             tb.add(semenu);
25875             
25876             
25877         }
25878          
25879         
25880         if (this.btns) {
25881             for(var i =0; i< this.btns.length;i++) {
25882                 var b = this.btns[i];
25883                 b.cls =  'x-edit-none';
25884                 b.scope = editor;
25885                 tb.add(b);
25886             }
25887         
25888         }
25889         
25890         
25891         
25892         // disable everything...
25893         
25894         this.tb.items.each(function(item){
25895            if(item.id != editor.frameId+ '-sourceedit'){
25896                 item.disable();
25897             }
25898         });
25899         this.rendered = true;
25900         
25901         // the all the btns;
25902         editor.on('editorevent', this.updateToolbar, this);
25903         // other toolbars need to implement this..
25904         //editor.on('editmodechange', this.updateToolbar, this);
25905     },
25906     
25907     
25908     
25909     /**
25910      * Protected method that will not generally be called directly. It triggers
25911      * a toolbar update by reading the markup state of the current selection in the editor.
25912      */
25913     updateToolbar: function(){
25914
25915         if(!this.editor.activated){
25916             this.editor.onFirstFocus();
25917             return;
25918         }
25919
25920         var btns = this.tb.items.map, 
25921             doc = this.editor.doc,
25922             frameId = this.editor.frameId;
25923
25924         if(!this.disable.font && !Roo.isSafari){
25925             /*
25926             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25927             if(name != this.fontSelect.dom.value){
25928                 this.fontSelect.dom.value = name;
25929             }
25930             */
25931         }
25932         if(!this.disable.format){
25933             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25934             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25935             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25936         }
25937         if(!this.disable.alignments){
25938             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25939             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25940             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25941         }
25942         if(!Roo.isSafari && !this.disable.lists){
25943             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25944             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25945         }
25946         
25947         var ans = this.editor.getAllAncestors();
25948         if (this.formatCombo) {
25949             
25950             
25951             var store = this.formatCombo.store;
25952             this.formatCombo.setValue("");
25953             for (var i =0; i < ans.length;i++) {
25954                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25955                     // select it..
25956                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25957                     break;
25958                 }
25959             }
25960         }
25961         
25962         
25963         
25964         // hides menus... - so this cant be on a menu...
25965         Roo.menu.MenuMgr.hideAll();
25966
25967         //this.editorsyncValue();
25968     },
25969    
25970     
25971     createFontOptions : function(){
25972         var buf = [], fs = this.fontFamilies, ff, lc;
25973         for(var i = 0, len = fs.length; i< len; i++){
25974             ff = fs[i];
25975             lc = ff.toLowerCase();
25976             buf.push(
25977                 '<option value="',lc,'" style="font-family:',ff,';"',
25978                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25979                     ff,
25980                 '</option>'
25981             );
25982         }
25983         return buf.join('');
25984     },
25985     
25986     toggleSourceEdit : function(sourceEditMode){
25987         if(sourceEditMode === undefined){
25988             sourceEditMode = !this.sourceEditMode;
25989         }
25990         this.sourceEditMode = sourceEditMode === true;
25991         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25992         // just toggle the button?
25993         if(btn.pressed !== this.editor.sourceEditMode){
25994             btn.toggle(this.editor.sourceEditMode);
25995             return;
25996         }
25997         
25998         if(this.sourceEditMode){
25999             this.tb.items.each(function(item){
26000                 if(item.cmd != 'sourceedit'){
26001                     item.disable();
26002                 }
26003             });
26004           
26005         }else{
26006             if(this.initialized){
26007                 this.tb.items.each(function(item){
26008                     item.enable();
26009                 });
26010             }
26011             
26012         }
26013         // tell the editor that it's been pressed..
26014         this.editor.toggleSourceEdit(sourceEditMode);
26015        
26016     },
26017      /**
26018      * Object collection of toolbar tooltips for the buttons in the editor. The key
26019      * is the command id associated with that button and the value is a valid QuickTips object.
26020      * For example:
26021 <pre><code>
26022 {
26023     bold : {
26024         title: 'Bold (Ctrl+B)',
26025         text: 'Make the selected text bold.',
26026         cls: 'x-html-editor-tip'
26027     },
26028     italic : {
26029         title: 'Italic (Ctrl+I)',
26030         text: 'Make the selected text italic.',
26031         cls: 'x-html-editor-tip'
26032     },
26033     ...
26034 </code></pre>
26035     * @type Object
26036      */
26037     buttonTips : {
26038         bold : {
26039             title: 'Bold (Ctrl+B)',
26040             text: 'Make the selected text bold.',
26041             cls: 'x-html-editor-tip'
26042         },
26043         italic : {
26044             title: 'Italic (Ctrl+I)',
26045             text: 'Make the selected text italic.',
26046             cls: 'x-html-editor-tip'
26047         },
26048         underline : {
26049             title: 'Underline (Ctrl+U)',
26050             text: 'Underline the selected text.',
26051             cls: 'x-html-editor-tip'
26052         },
26053         increasefontsize : {
26054             title: 'Grow Text',
26055             text: 'Increase the font size.',
26056             cls: 'x-html-editor-tip'
26057         },
26058         decreasefontsize : {
26059             title: 'Shrink Text',
26060             text: 'Decrease the font size.',
26061             cls: 'x-html-editor-tip'
26062         },
26063         backcolor : {
26064             title: 'Text Highlight Color',
26065             text: 'Change the background color of the selected text.',
26066             cls: 'x-html-editor-tip'
26067         },
26068         forecolor : {
26069             title: 'Font Color',
26070             text: 'Change the color of the selected text.',
26071             cls: 'x-html-editor-tip'
26072         },
26073         justifyleft : {
26074             title: 'Align Text Left',
26075             text: 'Align text to the left.',
26076             cls: 'x-html-editor-tip'
26077         },
26078         justifycenter : {
26079             title: 'Center Text',
26080             text: 'Center text in the editor.',
26081             cls: 'x-html-editor-tip'
26082         },
26083         justifyright : {
26084             title: 'Align Text Right',
26085             text: 'Align text to the right.',
26086             cls: 'x-html-editor-tip'
26087         },
26088         insertunorderedlist : {
26089             title: 'Bullet List',
26090             text: 'Start a bulleted list.',
26091             cls: 'x-html-editor-tip'
26092         },
26093         insertorderedlist : {
26094             title: 'Numbered List',
26095             text: 'Start a numbered list.',
26096             cls: 'x-html-editor-tip'
26097         },
26098         createlink : {
26099             title: 'Hyperlink',
26100             text: 'Make the selected text a hyperlink.',
26101             cls: 'x-html-editor-tip'
26102         },
26103         sourceedit : {
26104             title: 'Source Edit',
26105             text: 'Switch to source editing mode.',
26106             cls: 'x-html-editor-tip'
26107         }
26108     },
26109     // private
26110     onDestroy : function(){
26111         if(this.rendered){
26112             
26113             this.tb.items.each(function(item){
26114                 if(item.menu){
26115                     item.menu.removeAll();
26116                     if(item.menu.el){
26117                         item.menu.el.destroy();
26118                     }
26119                 }
26120                 item.destroy();
26121             });
26122              
26123         }
26124     },
26125     onFirstFocus: function() {
26126         this.tb.items.each(function(item){
26127            item.enable();
26128         });
26129     }
26130 });
26131
26132
26133
26134
26135 // <script type="text/javascript">
26136 /*
26137  * Based on
26138  * Ext JS Library 1.1.1
26139  * Copyright(c) 2006-2007, Ext JS, LLC.
26140  *  
26141  
26142  */
26143
26144  
26145 /**
26146  * @class Roo.form.HtmlEditor.ToolbarContext
26147  * Context Toolbar
26148  * 
26149  * Usage:
26150  *
26151  new Roo.form.HtmlEditor({
26152     ....
26153     toolbars : [
26154         { xtype: 'ToolbarStandard', styles : {} }
26155         { xtype: 'ToolbarContext', disable : {} }
26156     ]
26157 })
26158
26159      
26160  * 
26161  * @config : {Object} disable List of elements to disable.. (not done yet.)
26162  * @config : {Object} styles  Map of styles available.
26163  * 
26164  */
26165
26166 Roo.form.HtmlEditor.ToolbarContext = function(config)
26167 {
26168     
26169     Roo.apply(this, config);
26170     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26171     // dont call parent... till later.
26172     this.styles = this.styles || {};
26173 }
26174 Roo.form.HtmlEditor.ToolbarContext.types = {
26175     'IMG' : {
26176         width : {
26177             title: "Width",
26178             width: 40
26179         },
26180         height:  {
26181             title: "Height",
26182             width: 40
26183         },
26184         align: {
26185             title: "Align",
26186             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
26187             width : 80
26188             
26189         },
26190         border: {
26191             title: "Border",
26192             width: 40
26193         },
26194         alt: {
26195             title: "Alt",
26196             width: 120
26197         },
26198         src : {
26199             title: "Src",
26200             width: 220
26201         }
26202         
26203     },
26204     'A' : {
26205         name : {
26206             title: "Name",
26207             width: 50
26208         },
26209         href:  {
26210             title: "Href",
26211             width: 220
26212         } // border?
26213         
26214     },
26215     'TABLE' : {
26216         rows : {
26217             title: "Rows",
26218             width: 20
26219         },
26220         cols : {
26221             title: "Cols",
26222             width: 20
26223         },
26224         width : {
26225             title: "Width",
26226             width: 40
26227         },
26228         height : {
26229             title: "Height",
26230             width: 40
26231         },
26232         border : {
26233             title: "Border",
26234             width: 20
26235         }
26236     },
26237     'TD' : {
26238         width : {
26239             title: "Width",
26240             width: 40
26241         },
26242         height : {
26243             title: "Height",
26244             width: 40
26245         },   
26246         align: {
26247             title: "Align",
26248             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
26249             width: 80
26250         },
26251         valign: {
26252             title: "Valign",
26253             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
26254             width: 80
26255         },
26256         colspan: {
26257             title: "Colspan",
26258             width: 20
26259             
26260         }
26261     },
26262     'INPUT' : {
26263         name : {
26264             title: "name",
26265             width: 120
26266         },
26267         value : {
26268             title: "Value",
26269             width: 120
26270         },
26271         width : {
26272             title: "Width",
26273             width: 40
26274         }
26275     },
26276     'LABEL' : {
26277         'for' : {
26278             title: "For",
26279             width: 120
26280         }
26281     },
26282     'TEXTAREA' : {
26283           name : {
26284             title: "name",
26285             width: 120
26286         },
26287         rows : {
26288             title: "Rows",
26289             width: 20
26290         },
26291         cols : {
26292             title: "Cols",
26293             width: 20
26294         }
26295     },
26296     'SELECT' : {
26297         name : {
26298             title: "name",
26299             width: 120
26300         },
26301         selectoptions : {
26302             title: "Options",
26303             width: 200
26304         }
26305     },
26306     
26307     // should we really allow this??
26308     // should this just be 
26309     'BODY' : {
26310         title : {
26311             title: "title",
26312             width: 200,
26313             disabled : true
26314         }
26315     },
26316     '*' : {
26317         // empty..
26318     }
26319 };
26320
26321
26322
26323 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
26324     
26325     tb: false,
26326     
26327     rendered: false,
26328     
26329     editor : false,
26330     /**
26331      * @cfg {Object} disable  List of toolbar elements to disable
26332          
26333      */
26334     disable : false,
26335     /**
26336      * @cfg {Object} styles List of styles 
26337      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
26338      *
26339      * These must be defined in the page, so they get rendered correctly..
26340      * .headline { }
26341      * TD.underline { }
26342      * 
26343      */
26344     styles : false,
26345     
26346     
26347     
26348     toolbars : false,
26349     
26350     init : function(editor)
26351     {
26352         this.editor = editor;
26353         
26354         
26355         var fid = editor.frameId;
26356         var etb = this;
26357         function btn(id, toggle, handler){
26358             var xid = fid + '-'+ id ;
26359             return {
26360                 id : xid,
26361                 cmd : id,
26362                 cls : 'x-btn-icon x-edit-'+id,
26363                 enableToggle:toggle !== false,
26364                 scope: editor, // was editor...
26365                 handler:handler||editor.relayBtnCmd,
26366                 clickEvent:'mousedown',
26367                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26368                 tabIndex:-1
26369             };
26370         }
26371         // create a new element.
26372         var wdiv = editor.wrap.createChild({
26373                 tag: 'div'
26374             }, editor.wrap.dom.firstChild.nextSibling, true);
26375         
26376         // can we do this more than once??
26377         
26378          // stop form submits
26379       
26380  
26381         // disable everything...
26382         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26383         this.toolbars = {};
26384            
26385         for (var i in  ty) {
26386           
26387             this.toolbars[i] = this.buildToolbar(ty[i],i);
26388         }
26389         this.tb = this.toolbars.BODY;
26390         this.tb.el.show();
26391         this.buildFooter();
26392         this.footer.show();
26393         editor.on('hide', function( ) { this.footer.hide() }, this);
26394         editor.on('show', function( ) { this.footer.show() }, this);
26395         
26396          
26397         this.rendered = true;
26398         
26399         // the all the btns;
26400         editor.on('editorevent', this.updateToolbar, this);
26401         // other toolbars need to implement this..
26402         //editor.on('editmodechange', this.updateToolbar, this);
26403     },
26404     
26405     
26406     
26407     /**
26408      * Protected method that will not generally be called directly. It triggers
26409      * a toolbar update by reading the markup state of the current selection in the editor.
26410      */
26411     updateToolbar: function(editor,ev,sel){
26412
26413         //Roo.log(ev);
26414         // capture mouse up - this is handy for selecting images..
26415         // perhaps should go somewhere else...
26416         if(!this.editor.activated){
26417              this.editor.onFirstFocus();
26418             return;
26419         }
26420         
26421         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
26422         // selectNode - might want to handle IE?
26423         if (ev &&
26424             (ev.type == 'mouseup' || ev.type == 'click' ) &&
26425             ev.target && ev.target.tagName == 'IMG') {
26426             // they have click on an image...
26427             // let's see if we can change the selection...
26428             sel = ev.target;
26429          
26430               var nodeRange = sel.ownerDocument.createRange();
26431             try {
26432                 nodeRange.selectNode(sel);
26433             } catch (e) {
26434                 nodeRange.selectNodeContents(sel);
26435             }
26436             //nodeRange.collapse(true);
26437             var s = editor.win.getSelection();
26438             s.removeAllRanges();
26439             s.addRange(nodeRange);
26440         }  
26441         
26442       
26443         var updateFooter = sel ? false : true;
26444         
26445         
26446         var ans = this.editor.getAllAncestors();
26447         
26448         // pick
26449         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26450         
26451         if (!sel) { 
26452             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
26453             sel = sel ? sel : this.editor.doc.body;
26454             sel = sel.tagName.length ? sel : this.editor.doc.body;
26455             
26456         }
26457         // pick a menu that exists..
26458         var tn = sel.tagName.toUpperCase();
26459         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
26460         
26461         tn = sel.tagName.toUpperCase();
26462         
26463         var lastSel = this.tb.selectedNode
26464         
26465         this.tb.selectedNode = sel;
26466         
26467         // if current menu does not match..
26468         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
26469                 
26470             this.tb.el.hide();
26471             ///console.log("show: " + tn);
26472             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
26473             this.tb.el.show();
26474             // update name
26475             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
26476             
26477             
26478             // update attributes
26479             if (this.tb.fields) {
26480                 this.tb.fields.each(function(e) {
26481                    e.setValue(sel.getAttribute(e.attrname));
26482                 });
26483             }
26484             
26485             var hasStyles = false;
26486             for(var i in this.styles) {
26487                 hasStyles = true;
26488                 break;
26489             }
26490             
26491             // update styles
26492             if (hasStyles) { 
26493                 var st = this.tb.fields.item(0);
26494                 
26495                 st.store.removeAll();
26496                
26497                 
26498                 var cn = sel.className.split(/\s+/);
26499                 
26500                 var avs = [];
26501                 if (this.styles['*']) {
26502                     
26503                     Roo.each(this.styles['*'], function(v) {
26504                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26505                     });
26506                 }
26507                 if (this.styles[tn]) { 
26508                     Roo.each(this.styles[tn], function(v) {
26509                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26510                     });
26511                 }
26512                 
26513                 st.store.loadData(avs);
26514                 st.collapse();
26515                 st.setValue(cn);
26516             }
26517             // flag our selected Node.
26518             this.tb.selectedNode = sel;
26519            
26520            
26521             Roo.menu.MenuMgr.hideAll();
26522
26523         }
26524         
26525         if (!updateFooter) {
26526             return;
26527         }
26528         // update the footer
26529         //
26530         var html = '';
26531         
26532         this.footerEls = ans.reverse();
26533         Roo.each(this.footerEls, function(a,i) {
26534             if (!a) { return; }
26535             html += html.length ? ' &gt; '  :  '';
26536             
26537             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
26538             
26539         });
26540        
26541         // 
26542         var sz = this.footDisp.up('td').getSize();
26543         this.footDisp.dom.style.width = (sz.width -10) + 'px';
26544         this.footDisp.dom.style.marginLeft = '5px';
26545         
26546         this.footDisp.dom.style.overflow = 'hidden';
26547         
26548         this.footDisp.dom.innerHTML = html;
26549             
26550         //this.editorsyncValue();
26551     },
26552    
26553        
26554     // private
26555     onDestroy : function(){
26556         if(this.rendered){
26557             
26558             this.tb.items.each(function(item){
26559                 if(item.menu){
26560                     item.menu.removeAll();
26561                     if(item.menu.el){
26562                         item.menu.el.destroy();
26563                     }
26564                 }
26565                 item.destroy();
26566             });
26567              
26568         }
26569     },
26570     onFirstFocus: function() {
26571         // need to do this for all the toolbars..
26572         this.tb.items.each(function(item){
26573            item.enable();
26574         });
26575     },
26576     buildToolbar: function(tlist, nm)
26577     {
26578         var editor = this.editor;
26579          // create a new element.
26580         var wdiv = editor.wrap.createChild({
26581                 tag: 'div'
26582             }, editor.wrap.dom.firstChild.nextSibling, true);
26583         
26584        
26585         var tb = new Roo.Toolbar(wdiv);
26586         // add the name..
26587         
26588         tb.add(nm+ ":&nbsp;");
26589         
26590         var styles = [];
26591         for(var i in this.styles) {
26592             styles.push(i);
26593         }
26594         
26595         // styles...
26596         if (styles && styles.length) {
26597             
26598             // this needs a multi-select checkbox...
26599             tb.addField( new Roo.form.ComboBox({
26600                 store: new Roo.data.SimpleStore({
26601                     id : 'val',
26602                     fields: ['val', 'selected'],
26603                     data : [] 
26604                 }),
26605                 name : '-roo-edit-className',
26606                 attrname : 'className',
26607                 displayField:'val',
26608                 typeAhead: false,
26609                 mode: 'local',
26610                 editable : false,
26611                 triggerAction: 'all',
26612                 emptyText:'Select Style',
26613                 selectOnFocus:true,
26614                 width: 130,
26615                 listeners : {
26616                     'select': function(c, r, i) {
26617                         // initial support only for on class per el..
26618                         tb.selectedNode.className =  r ? r.get('val') : '';
26619                         editor.syncValue();
26620                     }
26621                 }
26622     
26623             }));
26624         }
26625             
26626         
26627         
26628         for (var i in tlist) {
26629             
26630             var item = tlist[i];
26631             tb.add(item.title + ":&nbsp;");
26632             
26633             
26634             
26635             
26636             if (item.opts) {
26637                 // opts == pulldown..
26638                 tb.addField( new Roo.form.ComboBox({
26639                     store: new Roo.data.SimpleStore({
26640                         id : 'val',
26641                         fields: ['val'],
26642                         data : item.opts  
26643                     }),
26644                     name : '-roo-edit-' + i,
26645                     attrname : i,
26646                     displayField:'val',
26647                     typeAhead: false,
26648                     mode: 'local',
26649                     editable : false,
26650                     triggerAction: 'all',
26651                     emptyText:'Select',
26652                     selectOnFocus:true,
26653                     width: item.width ? item.width  : 130,
26654                     listeners : {
26655                         'select': function(c, r, i) {
26656                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
26657                         }
26658                     }
26659
26660                 }));
26661                 continue;
26662                     
26663                  
26664                 
26665                 tb.addField( new Roo.form.TextField({
26666                     name: i,
26667                     width: 100,
26668                     //allowBlank:false,
26669                     value: ''
26670                 }));
26671                 continue;
26672             }
26673             tb.addField( new Roo.form.TextField({
26674                 name: '-roo-edit-' + i,
26675                 attrname : i,
26676                 
26677                 width: item.width,
26678                 //allowBlank:true,
26679                 value: '',
26680                 listeners: {
26681                     'change' : function(f, nv, ov) {
26682                         tb.selectedNode.setAttribute(f.attrname, nv);
26683                     }
26684                 }
26685             }));
26686              
26687         }
26688         tb.el.on('click', function(e){
26689             e.preventDefault(); // what does this do?
26690         });
26691         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
26692         tb.el.hide();
26693         tb.name = nm;
26694         // dont need to disable them... as they will get hidden
26695         return tb;
26696          
26697         
26698     },
26699     buildFooter : function()
26700     {
26701         
26702         var fel = this.editor.wrap.createChild();
26703         this.footer = new Roo.Toolbar(fel);
26704         // toolbar has scrolly on left / right?
26705         var footDisp= new Roo.Toolbar.Fill();
26706         var _t = this;
26707         this.footer.add(
26708             {
26709                 text : '&lt;',
26710                 xtype: 'Button',
26711                 handler : function() {
26712                     _t.footDisp.scrollTo('left',0,true)
26713                 }
26714             }
26715         );
26716         this.footer.add( footDisp );
26717         this.footer.add( 
26718             {
26719                 text : '&gt;',
26720                 xtype: 'Button',
26721                 handler : function() {
26722                     // no animation..
26723                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
26724                 }
26725             }
26726         );
26727         var fel = Roo.get(footDisp.el);
26728         fel.addClass('x-editor-context');
26729         this.footDispWrap = fel; 
26730         this.footDispWrap.overflow  = 'hidden';
26731         
26732         this.footDisp = fel.createChild();
26733         this.footDispWrap.on('click', this.onContextClick, this)
26734         
26735         
26736     },
26737     onContextClick : function (ev,dom)
26738     {
26739         ev.preventDefault();
26740         var  cn = dom.className;
26741         Roo.log(cn);
26742         if (!cn.match(/x-ed-loc-/)) {
26743             return;
26744         }
26745         var n = cn.split('-').pop();
26746         var ans = this.footerEls;
26747         var sel = ans[n];
26748         
26749          // pick
26750         var range = this.editor.createRange();
26751         
26752         range.selectNodeContents(sel);
26753         //range.selectNode(sel);
26754         
26755         
26756         var selection = this.editor.getSelection();
26757         selection.removeAllRanges();
26758         selection.addRange(range);
26759         
26760         
26761         
26762         this.updateToolbar(null, null, sel);
26763         
26764         
26765     }
26766     
26767     
26768     
26769     
26770     
26771 });
26772
26773
26774
26775
26776
26777 /*
26778  * Based on:
26779  * Ext JS Library 1.1.1
26780  * Copyright(c) 2006-2007, Ext JS, LLC.
26781  *
26782  * Originally Released Under LGPL - original licence link has changed is not relivant.
26783  *
26784  * Fork - LGPL
26785  * <script type="text/javascript">
26786  */
26787  
26788 /**
26789  * @class Roo.form.BasicForm
26790  * @extends Roo.util.Observable
26791  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
26792  * @constructor
26793  * @param {String/HTMLElement/Roo.Element} el The form element or its id
26794  * @param {Object} config Configuration options
26795  */
26796 Roo.form.BasicForm = function(el, config){
26797     this.allItems = [];
26798     this.childForms = [];
26799     Roo.apply(this, config);
26800     /*
26801      * The Roo.form.Field items in this form.
26802      * @type MixedCollection
26803      */
26804      
26805      
26806     this.items = new Roo.util.MixedCollection(false, function(o){
26807         return o.id || (o.id = Roo.id());
26808     });
26809     this.addEvents({
26810         /**
26811          * @event beforeaction
26812          * Fires before any action is performed. Return false to cancel the action.
26813          * @param {Form} this
26814          * @param {Action} action The action to be performed
26815          */
26816         beforeaction: true,
26817         /**
26818          * @event actionfailed
26819          * Fires when an action fails.
26820          * @param {Form} this
26821          * @param {Action} action The action that failed
26822          */
26823         actionfailed : true,
26824         /**
26825          * @event actioncomplete
26826          * Fires when an action is completed.
26827          * @param {Form} this
26828          * @param {Action} action The action that completed
26829          */
26830         actioncomplete : true
26831     });
26832     if(el){
26833         this.initEl(el);
26834     }
26835     Roo.form.BasicForm.superclass.constructor.call(this);
26836 };
26837
26838 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
26839     /**
26840      * @cfg {String} method
26841      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
26842      */
26843     /**
26844      * @cfg {DataReader} reader
26845      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
26846      * This is optional as there is built-in support for processing JSON.
26847      */
26848     /**
26849      * @cfg {DataReader} errorReader
26850      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
26851      * This is completely optional as there is built-in support for processing JSON.
26852      */
26853     /**
26854      * @cfg {String} url
26855      * The URL to use for form actions if one isn't supplied in the action options.
26856      */
26857     /**
26858      * @cfg {Boolean} fileUpload
26859      * Set to true if this form is a file upload.
26860      */
26861      
26862     /**
26863      * @cfg {Object} baseParams
26864      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
26865      */
26866      /**
26867      
26868     /**
26869      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26870      */
26871     timeout: 30,
26872
26873     // private
26874     activeAction : null,
26875
26876     /**
26877      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26878      * or setValues() data instead of when the form was first created.
26879      */
26880     trackResetOnLoad : false,
26881     
26882     
26883     /**
26884      * childForms - used for multi-tab forms
26885      * @type {Array}
26886      */
26887     childForms : false,
26888     
26889     /**
26890      * allItems - full list of fields.
26891      * @type {Array}
26892      */
26893     allItems : false,
26894     
26895     /**
26896      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26897      * element by passing it or its id or mask the form itself by passing in true.
26898      * @type Mixed
26899      */
26900     waitMsgTarget : false,
26901
26902     // private
26903     initEl : function(el){
26904         this.el = Roo.get(el);
26905         this.id = this.el.id || Roo.id();
26906         this.el.on('submit', this.onSubmit, this);
26907         this.el.addClass('x-form');
26908     },
26909
26910     // private
26911     onSubmit : function(e){
26912         e.stopEvent();
26913     },
26914
26915     /**
26916      * Returns true if client-side validation on the form is successful.
26917      * @return Boolean
26918      */
26919     isValid : function(){
26920         var valid = true;
26921         this.items.each(function(f){
26922            if(!f.validate()){
26923                valid = false;
26924            }
26925         });
26926         return valid;
26927     },
26928
26929     /**
26930      * Returns true if any fields in this form have changed since their original load.
26931      * @return Boolean
26932      */
26933     isDirty : function(){
26934         var dirty = false;
26935         this.items.each(function(f){
26936            if(f.isDirty()){
26937                dirty = true;
26938                return false;
26939            }
26940         });
26941         return dirty;
26942     },
26943
26944     /**
26945      * Performs a predefined action (submit or load) or custom actions you define on this form.
26946      * @param {String} actionName The name of the action type
26947      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26948      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26949      * accept other config options):
26950      * <pre>
26951 Property          Type             Description
26952 ----------------  ---------------  ----------------------------------------------------------------------------------
26953 url               String           The url for the action (defaults to the form's url)
26954 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26955 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26956 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26957                                    validate the form on the client (defaults to false)
26958      * </pre>
26959      * @return {BasicForm} this
26960      */
26961     doAction : function(action, options){
26962         if(typeof action == 'string'){
26963             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26964         }
26965         if(this.fireEvent('beforeaction', this, action) !== false){
26966             this.beforeAction(action);
26967             action.run.defer(100, action);
26968         }
26969         return this;
26970     },
26971
26972     /**
26973      * Shortcut to do a submit action.
26974      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26975      * @return {BasicForm} this
26976      */
26977     submit : function(options){
26978         this.doAction('submit', options);
26979         return this;
26980     },
26981
26982     /**
26983      * Shortcut to do a load action.
26984      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26985      * @return {BasicForm} this
26986      */
26987     load : function(options){
26988         this.doAction('load', options);
26989         return this;
26990     },
26991
26992     /**
26993      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26994      * @param {Record} record The record to edit
26995      * @return {BasicForm} this
26996      */
26997     updateRecord : function(record){
26998         record.beginEdit();
26999         var fs = record.fields;
27000         fs.each(function(f){
27001             var field = this.findField(f.name);
27002             if(field){
27003                 record.set(f.name, field.getValue());
27004             }
27005         }, this);
27006         record.endEdit();
27007         return this;
27008     },
27009
27010     /**
27011      * Loads an Roo.data.Record into this form.
27012      * @param {Record} record The record to load
27013      * @return {BasicForm} this
27014      */
27015     loadRecord : function(record){
27016         this.setValues(record.data);
27017         return this;
27018     },
27019
27020     // private
27021     beforeAction : function(action){
27022         var o = action.options;
27023         
27024        
27025         if(this.waitMsgTarget === true){
27026             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
27027         }else if(this.waitMsgTarget){
27028             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
27029             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
27030         }else {
27031             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
27032         }
27033          
27034     },
27035
27036     // private
27037     afterAction : function(action, success){
27038         this.activeAction = null;
27039         var o = action.options;
27040         
27041         if(this.waitMsgTarget === true){
27042             this.el.unmask();
27043         }else if(this.waitMsgTarget){
27044             this.waitMsgTarget.unmask();
27045         }else{
27046             Roo.MessageBox.updateProgress(1);
27047             Roo.MessageBox.hide();
27048         }
27049          
27050         if(success){
27051             if(o.reset){
27052                 this.reset();
27053             }
27054             Roo.callback(o.success, o.scope, [this, action]);
27055             this.fireEvent('actioncomplete', this, action);
27056             
27057         }else{
27058             
27059             // failure condition..
27060             // we have a scenario where updates need confirming.
27061             // eg. if a locking scenario exists..
27062             // we look for { errors : { needs_confirm : true }} in the response.
27063             if (
27064                 (typeof(action.result) != 'undefined')  &&
27065                 (typeof(action.result.errors) != 'undefined')  &&
27066                 (typeof(action.result.errors.needs_confirm) != 'undefined')
27067           ){
27068                 var _t = this;
27069                 Roo.MessageBox.confirm(
27070                     "Change requires confirmation",
27071                     action.result.errorMsg,
27072                     function(r) {
27073                         if (r != 'yes') {
27074                             return;
27075                         }
27076                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
27077                     }
27078                     
27079                 );
27080                 
27081                 
27082                 
27083                 return;
27084             }
27085             
27086             Roo.callback(o.failure, o.scope, [this, action]);
27087             // show an error message if no failed handler is set..
27088             if (!this.hasListener('actionfailed')) {
27089                 Roo.MessageBox.alert("Error",
27090                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
27091                         action.result.errorMsg :
27092                         "Saving Failed, please check your entries or try again"
27093                 );
27094             }
27095             
27096             this.fireEvent('actionfailed', this, action);
27097         }
27098         
27099     },
27100
27101     /**
27102      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
27103      * @param {String} id The value to search for
27104      * @return Field
27105      */
27106     findField : function(id){
27107         var field = this.items.get(id);
27108         if(!field){
27109             this.items.each(function(f){
27110                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
27111                     field = f;
27112                     return false;
27113                 }
27114             });
27115         }
27116         return field || null;
27117     },
27118
27119     /**
27120      * Add a secondary form to this one, 
27121      * Used to provide tabbed forms. One form is primary, with hidden values 
27122      * which mirror the elements from the other forms.
27123      * 
27124      * @param {Roo.form.Form} form to add.
27125      * 
27126      */
27127     addForm : function(form)
27128     {
27129        
27130         if (this.childForms.indexOf(form) > -1) {
27131             // already added..
27132             return;
27133         }
27134         this.childForms.push(form);
27135         var n = '';
27136         Roo.each(form.allItems, function (fe) {
27137             
27138             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
27139             if (this.findField(n)) { // already added..
27140                 return;
27141             }
27142             var add = new Roo.form.Hidden({
27143                 name : n
27144             });
27145             add.render(this.el);
27146             
27147             this.add( add );
27148         }, this);
27149         
27150     },
27151     /**
27152      * Mark fields in this form invalid in bulk.
27153      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
27154      * @return {BasicForm} this
27155      */
27156     markInvalid : function(errors){
27157         if(errors instanceof Array){
27158             for(var i = 0, len = errors.length; i < len; i++){
27159                 var fieldError = errors[i];
27160                 var f = this.findField(fieldError.id);
27161                 if(f){
27162                     f.markInvalid(fieldError.msg);
27163                 }
27164             }
27165         }else{
27166             var field, id;
27167             for(id in errors){
27168                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
27169                     field.markInvalid(errors[id]);
27170                 }
27171             }
27172         }
27173         Roo.each(this.childForms || [], function (f) {
27174             f.markInvalid(errors);
27175         });
27176         
27177         return this;
27178     },
27179
27180     /**
27181      * Set values for fields in this form in bulk.
27182      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
27183      * @return {BasicForm} this
27184      */
27185     setValues : function(values){
27186         if(values instanceof Array){ // array of objects
27187             for(var i = 0, len = values.length; i < len; i++){
27188                 var v = values[i];
27189                 var f = this.findField(v.id);
27190                 if(f){
27191                     f.setValue(v.value);
27192                     if(this.trackResetOnLoad){
27193                         f.originalValue = f.getValue();
27194                     }
27195                 }
27196             }
27197         }else{ // object hash
27198             var field, id;
27199             for(id in values){
27200                 if(typeof values[id] != 'function' && (field = this.findField(id))){
27201                     
27202                     if (field.setFromData && 
27203                         field.valueField && 
27204                         field.displayField &&
27205                         // combos' with local stores can 
27206                         // be queried via setValue()
27207                         // to set their value..
27208                         (field.store && !field.store.isLocal)
27209                         ) {
27210                         // it's a combo
27211                         var sd = { };
27212                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
27213                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
27214                         field.setFromData(sd);
27215                         
27216                     } else {
27217                         field.setValue(values[id]);
27218                     }
27219                     
27220                     
27221                     if(this.trackResetOnLoad){
27222                         field.originalValue = field.getValue();
27223                     }
27224                 }
27225             }
27226         }
27227          
27228         Roo.each(this.childForms || [], function (f) {
27229             f.setValues(values);
27230         });
27231                 
27232         return this;
27233     },
27234
27235     /**
27236      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
27237      * they are returned as an array.
27238      * @param {Boolean} asString
27239      * @return {Object}
27240      */
27241     getValues : function(asString){
27242         if (this.childForms) {
27243             // copy values from the child forms
27244             Roo.each(this.childForms, function (f) {
27245                 this.setValues(f.getValues());
27246             }, this);
27247         }
27248         
27249         
27250         
27251         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
27252         if(asString === true){
27253             return fs;
27254         }
27255         return Roo.urlDecode(fs);
27256     },
27257     
27258     /**
27259      * Returns the fields in this form as an object with key/value pairs. 
27260      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
27261      * @return {Object}
27262      */
27263     getFieldValues : function(with_hidden)
27264     {
27265         if (this.childForms) {
27266             // copy values from the child forms
27267             // should this call getFieldValues - probably not as we do not currently copy
27268             // hidden fields when we generate..
27269             Roo.each(this.childForms, function (f) {
27270                 this.setValues(f.getValues());
27271             }, this);
27272         }
27273         
27274         var ret = {};
27275         this.items.each(function(f){
27276             if (!f.getName()) {
27277                 return;
27278             }
27279             var v = f.getValue();
27280             // not sure if this supported any more..
27281             if ((typeof(v) == 'object') && f.getRawValue) {
27282                 v = f.getRawValue() ; // dates..
27283             }
27284             // combo boxes where name != hiddenName...
27285             if (f.name != f.getName()) {
27286                 ret[f.name] = f.getRawValue();
27287             }
27288             ret[f.getName()] = v;
27289         });
27290         
27291         return ret;
27292     },
27293
27294     /**
27295      * Clears all invalid messages in this form.
27296      * @return {BasicForm} this
27297      */
27298     clearInvalid : function(){
27299         this.items.each(function(f){
27300            f.clearInvalid();
27301         });
27302         
27303         Roo.each(this.childForms || [], function (f) {
27304             f.clearInvalid();
27305         });
27306         
27307         
27308         return this;
27309     },
27310
27311     /**
27312      * Resets this form.
27313      * @return {BasicForm} this
27314      */
27315     reset : function(){
27316         this.items.each(function(f){
27317             f.reset();
27318         });
27319         
27320         Roo.each(this.childForms || [], function (f) {
27321             f.reset();
27322         });
27323        
27324         
27325         return this;
27326     },
27327
27328     /**
27329      * Add Roo.form components to this form.
27330      * @param {Field} field1
27331      * @param {Field} field2 (optional)
27332      * @param {Field} etc (optional)
27333      * @return {BasicForm} this
27334      */
27335     add : function(){
27336         this.items.addAll(Array.prototype.slice.call(arguments, 0));
27337         return this;
27338     },
27339
27340
27341     /**
27342      * Removes a field from the items collection (does NOT remove its markup).
27343      * @param {Field} field
27344      * @return {BasicForm} this
27345      */
27346     remove : function(field){
27347         this.items.remove(field);
27348         return this;
27349     },
27350
27351     /**
27352      * Looks at the fields in this form, checks them for an id attribute,
27353      * and calls applyTo on the existing dom element with that id.
27354      * @return {BasicForm} this
27355      */
27356     render : function(){
27357         this.items.each(function(f){
27358             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
27359                 f.applyTo(f.id);
27360             }
27361         });
27362         return this;
27363     },
27364
27365     /**
27366      * Calls {@link Ext#apply} for all fields in this form with the passed object.
27367      * @param {Object} values
27368      * @return {BasicForm} this
27369      */
27370     applyToFields : function(o){
27371         this.items.each(function(f){
27372            Roo.apply(f, o);
27373         });
27374         return this;
27375     },
27376
27377     /**
27378      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
27379      * @param {Object} values
27380      * @return {BasicForm} this
27381      */
27382     applyIfToFields : function(o){
27383         this.items.each(function(f){
27384            Roo.applyIf(f, o);
27385         });
27386         return this;
27387     }
27388 });
27389
27390 // back compat
27391 Roo.BasicForm = Roo.form.BasicForm;/*
27392  * Based on:
27393  * Ext JS Library 1.1.1
27394  * Copyright(c) 2006-2007, Ext JS, LLC.
27395  *
27396  * Originally Released Under LGPL - original licence link has changed is not relivant.
27397  *
27398  * Fork - LGPL
27399  * <script type="text/javascript">
27400  */
27401
27402 /**
27403  * @class Roo.form.Form
27404  * @extends Roo.form.BasicForm
27405  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
27406  * @constructor
27407  * @param {Object} config Configuration options
27408  */
27409 Roo.form.Form = function(config){
27410     var xitems =  [];
27411     if (config.items) {
27412         xitems = config.items;
27413         delete config.items;
27414     }
27415    
27416     
27417     Roo.form.Form.superclass.constructor.call(this, null, config);
27418     this.url = this.url || this.action;
27419     if(!this.root){
27420         this.root = new Roo.form.Layout(Roo.applyIf({
27421             id: Roo.id()
27422         }, config));
27423     }
27424     this.active = this.root;
27425     /**
27426      * Array of all the buttons that have been added to this form via {@link addButton}
27427      * @type Array
27428      */
27429     this.buttons = [];
27430     this.allItems = [];
27431     this.addEvents({
27432         /**
27433          * @event clientvalidation
27434          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
27435          * @param {Form} this
27436          * @param {Boolean} valid true if the form has passed client-side validation
27437          */
27438         clientvalidation: true,
27439         /**
27440          * @event rendered
27441          * Fires when the form is rendered
27442          * @param {Roo.form.Form} form
27443          */
27444         rendered : true
27445     });
27446     
27447     if (this.progressUrl) {
27448             // push a hidden field onto the list of fields..
27449             this.addxtype( {
27450                     xns: Roo.form, 
27451                     xtype : 'Hidden', 
27452                     name : 'UPLOAD_IDENTIFIER' 
27453             });
27454         }
27455         
27456     
27457     Roo.each(xitems, this.addxtype, this);
27458     
27459     
27460     
27461 };
27462
27463 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
27464     /**
27465      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
27466      */
27467     /**
27468      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
27469      */
27470     /**
27471      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
27472      */
27473     buttonAlign:'center',
27474
27475     /**
27476      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
27477      */
27478     minButtonWidth:75,
27479
27480     /**
27481      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
27482      * This property cascades to child containers if not set.
27483      */
27484     labelAlign:'left',
27485
27486     /**
27487      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
27488      * fires a looping event with that state. This is required to bind buttons to the valid
27489      * state using the config value formBind:true on the button.
27490      */
27491     monitorValid : false,
27492
27493     /**
27494      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
27495      */
27496     monitorPoll : 200,
27497     
27498     /**
27499      * @cfg {String} progressUrl - Url to return progress data 
27500      */
27501     
27502     progressUrl : false,
27503   
27504     /**
27505      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
27506      * fields are added and the column is closed. If no fields are passed the column remains open
27507      * until end() is called.
27508      * @param {Object} config The config to pass to the column
27509      * @param {Field} field1 (optional)
27510      * @param {Field} field2 (optional)
27511      * @param {Field} etc (optional)
27512      * @return Column The column container object
27513      */
27514     column : function(c){
27515         var col = new Roo.form.Column(c);
27516         this.start(col);
27517         if(arguments.length > 1){ // duplicate code required because of Opera
27518             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27519             this.end();
27520         }
27521         return col;
27522     },
27523
27524     /**
27525      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
27526      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
27527      * until end() is called.
27528      * @param {Object} config The config to pass to the fieldset
27529      * @param {Field} field1 (optional)
27530      * @param {Field} field2 (optional)
27531      * @param {Field} etc (optional)
27532      * @return FieldSet The fieldset container object
27533      */
27534     fieldset : function(c){
27535         var fs = new Roo.form.FieldSet(c);
27536         this.start(fs);
27537         if(arguments.length > 1){ // duplicate code required because of Opera
27538             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27539             this.end();
27540         }
27541         return fs;
27542     },
27543
27544     /**
27545      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
27546      * fields are added and the container is closed. If no fields are passed the container remains open
27547      * until end() is called.
27548      * @param {Object} config The config to pass to the Layout
27549      * @param {Field} field1 (optional)
27550      * @param {Field} field2 (optional)
27551      * @param {Field} etc (optional)
27552      * @return Layout The container object
27553      */
27554     container : function(c){
27555         var l = new Roo.form.Layout(c);
27556         this.start(l);
27557         if(arguments.length > 1){ // duplicate code required because of Opera
27558             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27559             this.end();
27560         }
27561         return l;
27562     },
27563
27564     /**
27565      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
27566      * @param {Object} container A Roo.form.Layout or subclass of Layout
27567      * @return {Form} this
27568      */
27569     start : function(c){
27570         // cascade label info
27571         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
27572         this.active.stack.push(c);
27573         c.ownerCt = this.active;
27574         this.active = c;
27575         return this;
27576     },
27577
27578     /**
27579      * Closes the current open container
27580      * @return {Form} this
27581      */
27582     end : function(){
27583         if(this.active == this.root){
27584             return this;
27585         }
27586         this.active = this.active.ownerCt;
27587         return this;
27588     },
27589
27590     /**
27591      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
27592      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
27593      * as the label of the field.
27594      * @param {Field} field1
27595      * @param {Field} field2 (optional)
27596      * @param {Field} etc. (optional)
27597      * @return {Form} this
27598      */
27599     add : function(){
27600         this.active.stack.push.apply(this.active.stack, arguments);
27601         this.allItems.push.apply(this.allItems,arguments);
27602         var r = [];
27603         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
27604             if(a[i].isFormField){
27605                 r.push(a[i]);
27606             }
27607         }
27608         if(r.length > 0){
27609             Roo.form.Form.superclass.add.apply(this, r);
27610         }
27611         return this;
27612     },
27613     
27614
27615     
27616     
27617     
27618      /**
27619      * Find any element that has been added to a form, using it's ID or name
27620      * This can include framesets, columns etc. along with regular fields..
27621      * @param {String} id - id or name to find.
27622      
27623      * @return {Element} e - or false if nothing found.
27624      */
27625     findbyId : function(id)
27626     {
27627         var ret = false;
27628         if (!id) {
27629             return ret;
27630         }
27631         Roo.each(this.allItems, function(f){
27632             if (f.id == id || f.name == id ){
27633                 ret = f;
27634                 return false;
27635             }
27636         });
27637         return ret;
27638     },
27639
27640     
27641     
27642     /**
27643      * Render this form into the passed container. This should only be called once!
27644      * @param {String/HTMLElement/Element} container The element this component should be rendered into
27645      * @return {Form} this
27646      */
27647     render : function(ct)
27648     {
27649         
27650         
27651         
27652         ct = Roo.get(ct);
27653         var o = this.autoCreate || {
27654             tag: 'form',
27655             method : this.method || 'POST',
27656             id : this.id || Roo.id()
27657         };
27658         this.initEl(ct.createChild(o));
27659
27660         this.root.render(this.el);
27661         
27662        
27663              
27664         this.items.each(function(f){
27665             f.render('x-form-el-'+f.id);
27666         });
27667
27668         if(this.buttons.length > 0){
27669             // tables are required to maintain order and for correct IE layout
27670             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
27671                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
27672                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
27673             }}, null, true);
27674             var tr = tb.getElementsByTagName('tr')[0];
27675             for(var i = 0, len = this.buttons.length; i < len; i++) {
27676                 var b = this.buttons[i];
27677                 var td = document.createElement('td');
27678                 td.className = 'x-form-btn-td';
27679                 b.render(tr.appendChild(td));
27680             }
27681         }
27682         if(this.monitorValid){ // initialize after render
27683             this.startMonitoring();
27684         }
27685         this.fireEvent('rendered', this);
27686         return this;
27687     },
27688
27689     /**
27690      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
27691      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
27692      * object or a valid Roo.DomHelper element config
27693      * @param {Function} handler The function called when the button is clicked
27694      * @param {Object} scope (optional) The scope of the handler function
27695      * @return {Roo.Button}
27696      */
27697     addButton : function(config, handler, scope){
27698         var bc = {
27699             handler: handler,
27700             scope: scope,
27701             minWidth: this.minButtonWidth,
27702             hideParent:true
27703         };
27704         if(typeof config == "string"){
27705             bc.text = config;
27706         }else{
27707             Roo.apply(bc, config);
27708         }
27709         var btn = new Roo.Button(null, bc);
27710         this.buttons.push(btn);
27711         return btn;
27712     },
27713
27714      /**
27715      * Adds a series of form elements (using the xtype property as the factory method.
27716      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
27717      * @param {Object} config 
27718      */
27719     
27720     addxtype : function()
27721     {
27722         var ar = Array.prototype.slice.call(arguments, 0);
27723         var ret = false;
27724         for(var i = 0; i < ar.length; i++) {
27725             if (!ar[i]) {
27726                 continue; // skip -- if this happends something invalid got sent, we 
27727                 // should ignore it, as basically that interface element will not show up
27728                 // and that should be pretty obvious!!
27729             }
27730             
27731             if (Roo.form[ar[i].xtype]) {
27732                 ar[i].form = this;
27733                 var fe = Roo.factory(ar[i], Roo.form);
27734                 if (!ret) {
27735                     ret = fe;
27736                 }
27737                 fe.form = this;
27738                 if (fe.store) {
27739                     fe.store.form = this;
27740                 }
27741                 if (fe.isLayout) {  
27742                          
27743                     this.start(fe);
27744                     this.allItems.push(fe);
27745                     if (fe.items && fe.addxtype) {
27746                         fe.addxtype.apply(fe, fe.items);
27747                         delete fe.items;
27748                     }
27749                      this.end();
27750                     continue;
27751                 }
27752                 
27753                 
27754                  
27755                 this.add(fe);
27756               //  console.log('adding ' + ar[i].xtype);
27757             }
27758             if (ar[i].xtype == 'Button') {  
27759                 //console.log('adding button');
27760                 //console.log(ar[i]);
27761                 this.addButton(ar[i]);
27762                 this.allItems.push(fe);
27763                 continue;
27764             }
27765             
27766             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
27767                 alert('end is not supported on xtype any more, use items');
27768             //    this.end();
27769             //    //console.log('adding end');
27770             }
27771             
27772         }
27773         return ret;
27774     },
27775     
27776     /**
27777      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
27778      * option "monitorValid"
27779      */
27780     startMonitoring : function(){
27781         if(!this.bound){
27782             this.bound = true;
27783             Roo.TaskMgr.start({
27784                 run : this.bindHandler,
27785                 interval : this.monitorPoll || 200,
27786                 scope: this
27787             });
27788         }
27789     },
27790
27791     /**
27792      * Stops monitoring of the valid state of this form
27793      */
27794     stopMonitoring : function(){
27795         this.bound = false;
27796     },
27797
27798     // private
27799     bindHandler : function(){
27800         if(!this.bound){
27801             return false; // stops binding
27802         }
27803         var valid = true;
27804         this.items.each(function(f){
27805             if(!f.isValid(true)){
27806                 valid = false;
27807                 return false;
27808             }
27809         });
27810         for(var i = 0, len = this.buttons.length; i < len; i++){
27811             var btn = this.buttons[i];
27812             if(btn.formBind === true && btn.disabled === valid){
27813                 btn.setDisabled(!valid);
27814             }
27815         }
27816         this.fireEvent('clientvalidation', this, valid);
27817     }
27818     
27819     
27820     
27821     
27822     
27823     
27824     
27825     
27826 });
27827
27828
27829 // back compat
27830 Roo.Form = Roo.form.Form;
27831 /*
27832  * Based on:
27833  * Ext JS Library 1.1.1
27834  * Copyright(c) 2006-2007, Ext JS, LLC.
27835  *
27836  * Originally Released Under LGPL - original licence link has changed is not relivant.
27837  *
27838  * Fork - LGPL
27839  * <script type="text/javascript">
27840  */
27841  
27842  /**
27843  * @class Roo.form.Action
27844  * Internal Class used to handle form actions
27845  * @constructor
27846  * @param {Roo.form.BasicForm} el The form element or its id
27847  * @param {Object} config Configuration options
27848  */
27849  
27850  
27851 // define the action interface
27852 Roo.form.Action = function(form, options){
27853     this.form = form;
27854     this.options = options || {};
27855 };
27856 /**
27857  * Client Validation Failed
27858  * @const 
27859  */
27860 Roo.form.Action.CLIENT_INVALID = 'client';
27861 /**
27862  * Server Validation Failed
27863  * @const 
27864  */
27865  Roo.form.Action.SERVER_INVALID = 'server';
27866  /**
27867  * Connect to Server Failed
27868  * @const 
27869  */
27870 Roo.form.Action.CONNECT_FAILURE = 'connect';
27871 /**
27872  * Reading Data from Server Failed
27873  * @const 
27874  */
27875 Roo.form.Action.LOAD_FAILURE = 'load';
27876
27877 Roo.form.Action.prototype = {
27878     type : 'default',
27879     failureType : undefined,
27880     response : undefined,
27881     result : undefined,
27882
27883     // interface method
27884     run : function(options){
27885
27886     },
27887
27888     // interface method
27889     success : function(response){
27890
27891     },
27892
27893     // interface method
27894     handleResponse : function(response){
27895
27896     },
27897
27898     // default connection failure
27899     failure : function(response){
27900         
27901         this.response = response;
27902         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27903         this.form.afterAction(this, false);
27904     },
27905
27906     processResponse : function(response){
27907         this.response = response;
27908         if(!response.responseText){
27909             return true;
27910         }
27911         this.result = this.handleResponse(response);
27912         return this.result;
27913     },
27914
27915     // utility functions used internally
27916     getUrl : function(appendParams){
27917         var url = this.options.url || this.form.url || this.form.el.dom.action;
27918         if(appendParams){
27919             var p = this.getParams();
27920             if(p){
27921                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27922             }
27923         }
27924         return url;
27925     },
27926
27927     getMethod : function(){
27928         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27929     },
27930
27931     getParams : function(){
27932         var bp = this.form.baseParams;
27933         var p = this.options.params;
27934         if(p){
27935             if(typeof p == "object"){
27936                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27937             }else if(typeof p == 'string' && bp){
27938                 p += '&' + Roo.urlEncode(bp);
27939             }
27940         }else if(bp){
27941             p = Roo.urlEncode(bp);
27942         }
27943         return p;
27944     },
27945
27946     createCallback : function(){
27947         return {
27948             success: this.success,
27949             failure: this.failure,
27950             scope: this,
27951             timeout: (this.form.timeout*1000),
27952             upload: this.form.fileUpload ? this.success : undefined
27953         };
27954     }
27955 };
27956
27957 Roo.form.Action.Submit = function(form, options){
27958     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27959 };
27960
27961 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27962     type : 'submit',
27963
27964     haveProgress : false,
27965     uploadComplete : false,
27966     
27967     // uploadProgress indicator.
27968     uploadProgress : function()
27969     {
27970         if (!this.form.progressUrl) {
27971             return;
27972         }
27973         
27974         if (!this.haveProgress) {
27975             Roo.MessageBox.progress("Uploading", "Uploading");
27976         }
27977         if (this.uploadComplete) {
27978            Roo.MessageBox.hide();
27979            return;
27980         }
27981         
27982         this.haveProgress = true;
27983    
27984         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27985         
27986         var c = new Roo.data.Connection();
27987         c.request({
27988             url : this.form.progressUrl,
27989             params: {
27990                 id : uid
27991             },
27992             method: 'GET',
27993             success : function(req){
27994                //console.log(data);
27995                 var rdata = false;
27996                 var edata;
27997                 try  {
27998                    rdata = Roo.decode(req.responseText)
27999                 } catch (e) {
28000                     Roo.log("Invalid data from server..");
28001                     Roo.log(edata);
28002                     return;
28003                 }
28004                 if (!rdata || !rdata.success) {
28005                     Roo.log(rdata);
28006                     return;
28007                 }
28008                 var data = rdata.data;
28009                 
28010                 if (this.uploadComplete) {
28011                    Roo.MessageBox.hide();
28012                    return;
28013                 }
28014                    
28015                 if (data){
28016                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
28017                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
28018                     );
28019                 }
28020                 this.uploadProgress.defer(2000,this);
28021             },
28022        
28023             failure: function(data) {
28024                 Roo.log('progress url failed ');
28025                 Roo.log(data);
28026             },
28027             scope : this
28028         });
28029            
28030     },
28031     
28032     
28033     run : function()
28034     {
28035         // run get Values on the form, so it syncs any secondary forms.
28036         this.form.getValues();
28037         
28038         var o = this.options;
28039         var method = this.getMethod();
28040         var isPost = method == 'POST';
28041         if(o.clientValidation === false || this.form.isValid()){
28042             
28043             if (this.form.progressUrl) {
28044                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
28045                     (new Date() * 1) + '' + Math.random());
28046                     
28047             } 
28048             
28049             
28050             Roo.Ajax.request(Roo.apply(this.createCallback(), {
28051                 form:this.form.el.dom,
28052                 url:this.getUrl(!isPost),
28053                 method: method,
28054                 params:isPost ? this.getParams() : null,
28055                 isUpload: this.form.fileUpload
28056             }));
28057             
28058             this.uploadProgress();
28059
28060         }else if (o.clientValidation !== false){ // client validation failed
28061             this.failureType = Roo.form.Action.CLIENT_INVALID;
28062             this.form.afterAction(this, false);
28063         }
28064     },
28065
28066     success : function(response)
28067     {
28068         this.uploadComplete= true;
28069         if (this.haveProgress) {
28070             Roo.MessageBox.hide();
28071         }
28072         
28073         
28074         var result = this.processResponse(response);
28075         if(result === true || result.success){
28076             this.form.afterAction(this, true);
28077             return;
28078         }
28079         if(result.errors){
28080             this.form.markInvalid(result.errors);
28081             this.failureType = Roo.form.Action.SERVER_INVALID;
28082         }
28083         this.form.afterAction(this, false);
28084     },
28085     failure : function(response)
28086     {
28087         this.uploadComplete= true;
28088         if (this.haveProgress) {
28089             Roo.MessageBox.hide();
28090         }
28091         
28092         this.response = response;
28093         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28094         this.form.afterAction(this, false);
28095     },
28096     
28097     handleResponse : function(response){
28098         if(this.form.errorReader){
28099             var rs = this.form.errorReader.read(response);
28100             var errors = [];
28101             if(rs.records){
28102                 for(var i = 0, len = rs.records.length; i < len; i++) {
28103                     var r = rs.records[i];
28104                     errors[i] = r.data;
28105                 }
28106             }
28107             if(errors.length < 1){
28108                 errors = null;
28109             }
28110             return {
28111                 success : rs.success,
28112                 errors : errors
28113             };
28114         }
28115         var ret = false;
28116         try {
28117             ret = Roo.decode(response.responseText);
28118         } catch (e) {
28119             ret = {
28120                 success: false,
28121                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
28122                 errors : []
28123             };
28124         }
28125         return ret;
28126         
28127     }
28128 });
28129
28130
28131 Roo.form.Action.Load = function(form, options){
28132     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
28133     this.reader = this.form.reader;
28134 };
28135
28136 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
28137     type : 'load',
28138
28139     run : function(){
28140         
28141         Roo.Ajax.request(Roo.apply(
28142                 this.createCallback(), {
28143                     method:this.getMethod(),
28144                     url:this.getUrl(false),
28145                     params:this.getParams()
28146         }));
28147     },
28148
28149     success : function(response){
28150         
28151         var result = this.processResponse(response);
28152         if(result === true || !result.success || !result.data){
28153             this.failureType = Roo.form.Action.LOAD_FAILURE;
28154             this.form.afterAction(this, false);
28155             return;
28156         }
28157         this.form.clearInvalid();
28158         this.form.setValues(result.data);
28159         this.form.afterAction(this, true);
28160     },
28161
28162     handleResponse : function(response){
28163         if(this.form.reader){
28164             var rs = this.form.reader.read(response);
28165             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
28166             return {
28167                 success : rs.success,
28168                 data : data
28169             };
28170         }
28171         return Roo.decode(response.responseText);
28172     }
28173 });
28174
28175 Roo.form.Action.ACTION_TYPES = {
28176     'load' : Roo.form.Action.Load,
28177     'submit' : Roo.form.Action.Submit
28178 };/*
28179  * Based on:
28180  * Ext JS Library 1.1.1
28181  * Copyright(c) 2006-2007, Ext JS, LLC.
28182  *
28183  * Originally Released Under LGPL - original licence link has changed is not relivant.
28184  *
28185  * Fork - LGPL
28186  * <script type="text/javascript">
28187  */
28188  
28189 /**
28190  * @class Roo.form.Layout
28191  * @extends Roo.Component
28192  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
28193  * @constructor
28194  * @param {Object} config Configuration options
28195  */
28196 Roo.form.Layout = function(config){
28197     var xitems = [];
28198     if (config.items) {
28199         xitems = config.items;
28200         delete config.items;
28201     }
28202     Roo.form.Layout.superclass.constructor.call(this, config);
28203     this.stack = [];
28204     Roo.each(xitems, this.addxtype, this);
28205      
28206 };
28207
28208 Roo.extend(Roo.form.Layout, Roo.Component, {
28209     /**
28210      * @cfg {String/Object} autoCreate
28211      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
28212      */
28213     /**
28214      * @cfg {String/Object/Function} style
28215      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
28216      * a function which returns such a specification.
28217      */
28218     /**
28219      * @cfg {String} labelAlign
28220      * Valid values are "left," "top" and "right" (defaults to "left")
28221      */
28222     /**
28223      * @cfg {Number} labelWidth
28224      * Fixed width in pixels of all field labels (defaults to undefined)
28225      */
28226     /**
28227      * @cfg {Boolean} clear
28228      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
28229      */
28230     clear : true,
28231     /**
28232      * @cfg {String} labelSeparator
28233      * The separator to use after field labels (defaults to ':')
28234      */
28235     labelSeparator : ':',
28236     /**
28237      * @cfg {Boolean} hideLabels
28238      * True to suppress the display of field labels in this layout (defaults to false)
28239      */
28240     hideLabels : false,
28241
28242     // private
28243     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
28244     
28245     isLayout : true,
28246     
28247     // private
28248     onRender : function(ct, position){
28249         if(this.el){ // from markup
28250             this.el = Roo.get(this.el);
28251         }else {  // generate
28252             var cfg = this.getAutoCreate();
28253             this.el = ct.createChild(cfg, position);
28254         }
28255         if(this.style){
28256             this.el.applyStyles(this.style);
28257         }
28258         if(this.labelAlign){
28259             this.el.addClass('x-form-label-'+this.labelAlign);
28260         }
28261         if(this.hideLabels){
28262             this.labelStyle = "display:none";
28263             this.elementStyle = "padding-left:0;";
28264         }else{
28265             if(typeof this.labelWidth == 'number'){
28266                 this.labelStyle = "width:"+this.labelWidth+"px;";
28267                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
28268             }
28269             if(this.labelAlign == 'top'){
28270                 this.labelStyle = "width:auto;";
28271                 this.elementStyle = "padding-left:0;";
28272             }
28273         }
28274         var stack = this.stack;
28275         var slen = stack.length;
28276         if(slen > 0){
28277             if(!this.fieldTpl){
28278                 var t = new Roo.Template(
28279                     '<div class="x-form-item {5}">',
28280                         '<label for="{0}" style="{2}">{1}{4}</label>',
28281                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28282                         '</div>',
28283                     '</div><div class="x-form-clear-left"></div>'
28284                 );
28285                 t.disableFormats = true;
28286                 t.compile();
28287                 Roo.form.Layout.prototype.fieldTpl = t;
28288             }
28289             for(var i = 0; i < slen; i++) {
28290                 if(stack[i].isFormField){
28291                     this.renderField(stack[i]);
28292                 }else{
28293                     this.renderComponent(stack[i]);
28294                 }
28295             }
28296         }
28297         if(this.clear){
28298             this.el.createChild({cls:'x-form-clear'});
28299         }
28300     },
28301
28302     // private
28303     renderField : function(f){
28304         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
28305                f.id, //0
28306                f.fieldLabel, //1
28307                f.labelStyle||this.labelStyle||'', //2
28308                this.elementStyle||'', //3
28309                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
28310                f.itemCls||this.itemCls||''  //5
28311        ], true).getPrevSibling());
28312     },
28313
28314     // private
28315     renderComponent : function(c){
28316         c.render(c.isLayout ? this.el : this.el.createChild());    
28317     },
28318     /**
28319      * Adds a object form elements (using the xtype property as the factory method.)
28320      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
28321      * @param {Object} config 
28322      */
28323     addxtype : function(o)
28324     {
28325         // create the lement.
28326         o.form = this.form;
28327         var fe = Roo.factory(o, Roo.form);
28328         this.form.allItems.push(fe);
28329         this.stack.push(fe);
28330         
28331         if (fe.isFormField) {
28332             this.form.items.add(fe);
28333         }
28334          
28335         return fe;
28336     }
28337 });
28338
28339 /**
28340  * @class Roo.form.Column
28341  * @extends Roo.form.Layout
28342  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
28343  * @constructor
28344  * @param {Object} config Configuration options
28345  */
28346 Roo.form.Column = function(config){
28347     Roo.form.Column.superclass.constructor.call(this, config);
28348 };
28349
28350 Roo.extend(Roo.form.Column, Roo.form.Layout, {
28351     /**
28352      * @cfg {Number/String} width
28353      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28354      */
28355     /**
28356      * @cfg {String/Object} autoCreate
28357      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
28358      */
28359
28360     // private
28361     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
28362
28363     // private
28364     onRender : function(ct, position){
28365         Roo.form.Column.superclass.onRender.call(this, ct, position);
28366         if(this.width){
28367             this.el.setWidth(this.width);
28368         }
28369     }
28370 });
28371
28372
28373 /**
28374  * @class Roo.form.Row
28375  * @extends Roo.form.Layout
28376  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
28377  * @constructor
28378  * @param {Object} config Configuration options
28379  */
28380
28381  
28382 Roo.form.Row = function(config){
28383     Roo.form.Row.superclass.constructor.call(this, config);
28384 };
28385  
28386 Roo.extend(Roo.form.Row, Roo.form.Layout, {
28387       /**
28388      * @cfg {Number/String} width
28389      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28390      */
28391     /**
28392      * @cfg {Number/String} height
28393      * The fixed height of the column in pixels or CSS value (defaults to "auto")
28394      */
28395     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
28396     
28397     padWidth : 20,
28398     // private
28399     onRender : function(ct, position){
28400         //console.log('row render');
28401         if(!this.rowTpl){
28402             var t = new Roo.Template(
28403                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
28404                     '<label for="{0}" style="{2}">{1}{4}</label>',
28405                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28406                     '</div>',
28407                 '</div>'
28408             );
28409             t.disableFormats = true;
28410             t.compile();
28411             Roo.form.Layout.prototype.rowTpl = t;
28412         }
28413         this.fieldTpl = this.rowTpl;
28414         
28415         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
28416         var labelWidth = 100;
28417         
28418         if ((this.labelAlign != 'top')) {
28419             if (typeof this.labelWidth == 'number') {
28420                 labelWidth = this.labelWidth
28421             }
28422             this.padWidth =  20 + labelWidth;
28423             
28424         }
28425         
28426         Roo.form.Column.superclass.onRender.call(this, ct, position);
28427         if(this.width){
28428             this.el.setWidth(this.width);
28429         }
28430         if(this.height){
28431             this.el.setHeight(this.height);
28432         }
28433     },
28434     
28435     // private
28436     renderField : function(f){
28437         f.fieldEl = this.fieldTpl.append(this.el, [
28438                f.id, f.fieldLabel,
28439                f.labelStyle||this.labelStyle||'',
28440                this.elementStyle||'',
28441                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
28442                f.itemCls||this.itemCls||'',
28443                f.width ? f.width + this.padWidth : 160 + this.padWidth
28444        ],true);
28445     }
28446 });
28447  
28448
28449 /**
28450  * @class Roo.form.FieldSet
28451  * @extends Roo.form.Layout
28452  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
28453  * @constructor
28454  * @param {Object} config Configuration options
28455  */
28456 Roo.form.FieldSet = function(config){
28457     Roo.form.FieldSet.superclass.constructor.call(this, config);
28458 };
28459
28460 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
28461     /**
28462      * @cfg {String} legend
28463      * The text to display as the legend for the FieldSet (defaults to '')
28464      */
28465     /**
28466      * @cfg {String/Object} autoCreate
28467      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
28468      */
28469
28470     // private
28471     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
28472
28473     // private
28474     onRender : function(ct, position){
28475         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
28476         if(this.legend){
28477             this.setLegend(this.legend);
28478         }
28479     },
28480
28481     // private
28482     setLegend : function(text){
28483         if(this.rendered){
28484             this.el.child('legend').update(text);
28485         }
28486     }
28487 });/*
28488  * Based on:
28489  * Ext JS Library 1.1.1
28490  * Copyright(c) 2006-2007, Ext JS, LLC.
28491  *
28492  * Originally Released Under LGPL - original licence link has changed is not relivant.
28493  *
28494  * Fork - LGPL
28495  * <script type="text/javascript">
28496  */
28497 /**
28498  * @class Roo.form.VTypes
28499  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
28500  * @singleton
28501  */
28502 Roo.form.VTypes = function(){
28503     // closure these in so they are only created once.
28504     var alpha = /^[a-zA-Z_]+$/;
28505     var alphanum = /^[a-zA-Z0-9_]+$/;
28506     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
28507     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
28508
28509     // All these messages and functions are configurable
28510     return {
28511         /**
28512          * The function used to validate email addresses
28513          * @param {String} value The email address
28514          */
28515         'email' : function(v){
28516             return email.test(v);
28517         },
28518         /**
28519          * The error text to display when the email validation function returns false
28520          * @type String
28521          */
28522         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
28523         /**
28524          * The keystroke filter mask to be applied on email input
28525          * @type RegExp
28526          */
28527         'emailMask' : /[a-z0-9_\.\-@]/i,
28528
28529         /**
28530          * The function used to validate URLs
28531          * @param {String} value The URL
28532          */
28533         'url' : function(v){
28534             return url.test(v);
28535         },
28536         /**
28537          * The error text to display when the url validation function returns false
28538          * @type String
28539          */
28540         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
28541         
28542         /**
28543          * The function used to validate alpha values
28544          * @param {String} value The value
28545          */
28546         'alpha' : function(v){
28547             return alpha.test(v);
28548         },
28549         /**
28550          * The error text to display when the alpha validation function returns false
28551          * @type String
28552          */
28553         'alphaText' : 'This field should only contain letters and _',
28554         /**
28555          * The keystroke filter mask to be applied on alpha input
28556          * @type RegExp
28557          */
28558         'alphaMask' : /[a-z_]/i,
28559
28560         /**
28561          * The function used to validate alphanumeric values
28562          * @param {String} value The value
28563          */
28564         'alphanum' : function(v){
28565             return alphanum.test(v);
28566         },
28567         /**
28568          * The error text to display when the alphanumeric validation function returns false
28569          * @type String
28570          */
28571         'alphanumText' : 'This field should only contain letters, numbers and _',
28572         /**
28573          * The keystroke filter mask to be applied on alphanumeric input
28574          * @type RegExp
28575          */
28576         'alphanumMask' : /[a-z0-9_]/i
28577     };
28578 }();//<script type="text/javascript">
28579
28580 /**
28581  * @class Roo.form.FCKeditor
28582  * @extends Roo.form.TextArea
28583  * Wrapper around the FCKEditor http://www.fckeditor.net
28584  * @constructor
28585  * Creates a new FCKeditor
28586  * @param {Object} config Configuration options
28587  */
28588 Roo.form.FCKeditor = function(config){
28589     Roo.form.FCKeditor.superclass.constructor.call(this, config);
28590     this.addEvents({
28591          /**
28592          * @event editorinit
28593          * Fired when the editor is initialized - you can add extra handlers here..
28594          * @param {FCKeditor} this
28595          * @param {Object} the FCK object.
28596          */
28597         editorinit : true
28598     });
28599     
28600     
28601 };
28602 Roo.form.FCKeditor.editors = { };
28603 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
28604 {
28605     //defaultAutoCreate : {
28606     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
28607     //},
28608     // private
28609     /**
28610      * @cfg {Object} fck options - see fck manual for details.
28611      */
28612     fckconfig : false,
28613     
28614     /**
28615      * @cfg {Object} fck toolbar set (Basic or Default)
28616      */
28617     toolbarSet : 'Basic',
28618     /**
28619      * @cfg {Object} fck BasePath
28620      */ 
28621     basePath : '/fckeditor/',
28622     
28623     
28624     frame : false,
28625     
28626     value : '',
28627     
28628    
28629     onRender : function(ct, position)
28630     {
28631         if(!this.el){
28632             this.defaultAutoCreate = {
28633                 tag: "textarea",
28634                 style:"width:300px;height:60px;",
28635                 autocomplete: "off"
28636             };
28637         }
28638         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
28639         /*
28640         if(this.grow){
28641             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
28642             if(this.preventScrollbars){
28643                 this.el.setStyle("overflow", "hidden");
28644             }
28645             this.el.setHeight(this.growMin);
28646         }
28647         */
28648         //console.log('onrender' + this.getId() );
28649         Roo.form.FCKeditor.editors[this.getId()] = this;
28650          
28651
28652         this.replaceTextarea() ;
28653         
28654     },
28655     
28656     getEditor : function() {
28657         return this.fckEditor;
28658     },
28659     /**
28660      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
28661      * @param {Mixed} value The value to set
28662      */
28663     
28664     
28665     setValue : function(value)
28666     {
28667         //console.log('setValue: ' + value);
28668         
28669         if(typeof(value) == 'undefined') { // not sure why this is happending...
28670             return;
28671         }
28672         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28673         
28674         //if(!this.el || !this.getEditor()) {
28675         //    this.value = value;
28676             //this.setValue.defer(100,this,[value]);    
28677         //    return;
28678         //} 
28679         
28680         if(!this.getEditor()) {
28681             return;
28682         }
28683         
28684         this.getEditor().SetData(value);
28685         
28686         //
28687
28688     },
28689
28690     /**
28691      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
28692      * @return {Mixed} value The field value
28693      */
28694     getValue : function()
28695     {
28696         
28697         if (this.frame && this.frame.dom.style.display == 'none') {
28698             return Roo.form.FCKeditor.superclass.getValue.call(this);
28699         }
28700         
28701         if(!this.el || !this.getEditor()) {
28702            
28703            // this.getValue.defer(100,this); 
28704             return this.value;
28705         }
28706        
28707         
28708         var value=this.getEditor().GetData();
28709         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28710         return Roo.form.FCKeditor.superclass.getValue.call(this);
28711         
28712
28713     },
28714
28715     /**
28716      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
28717      * @return {Mixed} value The field value
28718      */
28719     getRawValue : function()
28720     {
28721         if (this.frame && this.frame.dom.style.display == 'none') {
28722             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28723         }
28724         
28725         if(!this.el || !this.getEditor()) {
28726             //this.getRawValue.defer(100,this); 
28727             return this.value;
28728             return;
28729         }
28730         
28731         
28732         
28733         var value=this.getEditor().GetData();
28734         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
28735         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28736          
28737     },
28738     
28739     setSize : function(w,h) {
28740         
28741         
28742         
28743         //if (this.frame && this.frame.dom.style.display == 'none') {
28744         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28745         //    return;
28746         //}
28747         //if(!this.el || !this.getEditor()) {
28748         //    this.setSize.defer(100,this, [w,h]); 
28749         //    return;
28750         //}
28751         
28752         
28753         
28754         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28755         
28756         this.frame.dom.setAttribute('width', w);
28757         this.frame.dom.setAttribute('height', h);
28758         this.frame.setSize(w,h);
28759         
28760     },
28761     
28762     toggleSourceEdit : function(value) {
28763         
28764       
28765          
28766         this.el.dom.style.display = value ? '' : 'none';
28767         this.frame.dom.style.display = value ?  'none' : '';
28768         
28769     },
28770     
28771     
28772     focus: function(tag)
28773     {
28774         if (this.frame.dom.style.display == 'none') {
28775             return Roo.form.FCKeditor.superclass.focus.call(this);
28776         }
28777         if(!this.el || !this.getEditor()) {
28778             this.focus.defer(100,this, [tag]); 
28779             return;
28780         }
28781         
28782         
28783         
28784         
28785         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
28786         this.getEditor().Focus();
28787         if (tgs.length) {
28788             if (!this.getEditor().Selection.GetSelection()) {
28789                 this.focus.defer(100,this, [tag]); 
28790                 return;
28791             }
28792             
28793             
28794             var r = this.getEditor().EditorDocument.createRange();
28795             r.setStart(tgs[0],0);
28796             r.setEnd(tgs[0],0);
28797             this.getEditor().Selection.GetSelection().removeAllRanges();
28798             this.getEditor().Selection.GetSelection().addRange(r);
28799             this.getEditor().Focus();
28800         }
28801         
28802     },
28803     
28804     
28805     
28806     replaceTextarea : function()
28807     {
28808         if ( document.getElementById( this.getId() + '___Frame' ) )
28809             return ;
28810         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
28811         //{
28812             // We must check the elements firstly using the Id and then the name.
28813         var oTextarea = document.getElementById( this.getId() );
28814         
28815         var colElementsByName = document.getElementsByName( this.getId() ) ;
28816          
28817         oTextarea.style.display = 'none' ;
28818
28819         if ( oTextarea.tabIndex ) {            
28820             this.TabIndex = oTextarea.tabIndex ;
28821         }
28822         
28823         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
28824         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
28825         this.frame = Roo.get(this.getId() + '___Frame')
28826     },
28827     
28828     _getConfigHtml : function()
28829     {
28830         var sConfig = '' ;
28831
28832         for ( var o in this.fckconfig ) {
28833             sConfig += sConfig.length > 0  ? '&amp;' : '';
28834             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
28835         }
28836
28837         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
28838     },
28839     
28840     
28841     _getIFrameHtml : function()
28842     {
28843         var sFile = 'fckeditor.html' ;
28844         /* no idea what this is about..
28845         try
28846         {
28847             if ( (/fcksource=true/i).test( window.top.location.search ) )
28848                 sFile = 'fckeditor.original.html' ;
28849         }
28850         catch (e) { 
28851         */
28852
28853         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
28854         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
28855         
28856         
28857         var html = '<iframe id="' + this.getId() +
28858             '___Frame" src="' + sLink +
28859             '" width="' + this.width +
28860             '" height="' + this.height + '"' +
28861             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
28862             ' frameborder="0" scrolling="no"></iframe>' ;
28863
28864         return html ;
28865     },
28866     
28867     _insertHtmlBefore : function( html, element )
28868     {
28869         if ( element.insertAdjacentHTML )       {
28870             // IE
28871             element.insertAdjacentHTML( 'beforeBegin', html ) ;
28872         } else { // Gecko
28873             var oRange = document.createRange() ;
28874             oRange.setStartBefore( element ) ;
28875             var oFragment = oRange.createContextualFragment( html );
28876             element.parentNode.insertBefore( oFragment, element ) ;
28877         }
28878     }
28879     
28880     
28881   
28882     
28883     
28884     
28885     
28886
28887 });
28888
28889 //Roo.reg('fckeditor', Roo.form.FCKeditor);
28890
28891 function FCKeditor_OnComplete(editorInstance){
28892     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
28893     f.fckEditor = editorInstance;
28894     //console.log("loaded");
28895     f.fireEvent('editorinit', f, editorInstance);
28896
28897   
28898
28899  
28900
28901
28902
28903
28904
28905
28906
28907
28908
28909
28910
28911
28912
28913
28914
28915 //<script type="text/javascript">
28916 /**
28917  * @class Roo.form.GridField
28918  * @extends Roo.form.Field
28919  * Embed a grid (or editable grid into a form)
28920  * STATUS ALPHA
28921  * 
28922  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28923  * it needs 
28924  * xgrid.store = Roo.data.Store
28925  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28926  * xgrid.store.reader = Roo.data.JsonReader 
28927  * 
28928  * 
28929  * @constructor
28930  * Creates a new GridField
28931  * @param {Object} config Configuration options
28932  */
28933 Roo.form.GridField = function(config){
28934     Roo.form.GridField.superclass.constructor.call(this, config);
28935      
28936 };
28937
28938 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28939     /**
28940      * @cfg {Number} width  - used to restrict width of grid..
28941      */
28942     width : 100,
28943     /**
28944      * @cfg {Number} height - used to restrict height of grid..
28945      */
28946     height : 50,
28947      /**
28948      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28949          * 
28950          *}
28951      */
28952     xgrid : false, 
28953     /**
28954      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28955      * {tag: "input", type: "checkbox", autocomplete: "off"})
28956      */
28957    // defaultAutoCreate : { tag: 'div' },
28958     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28959     /**
28960      * @cfg {String} addTitle Text to include for adding a title.
28961      */
28962     addTitle : false,
28963     //
28964     onResize : function(){
28965         Roo.form.Field.superclass.onResize.apply(this, arguments);
28966     },
28967
28968     initEvents : function(){
28969         // Roo.form.Checkbox.superclass.initEvents.call(this);
28970         // has no events...
28971        
28972     },
28973
28974
28975     getResizeEl : function(){
28976         return this.wrap;
28977     },
28978
28979     getPositionEl : function(){
28980         return this.wrap;
28981     },
28982
28983     // private
28984     onRender : function(ct, position){
28985         
28986         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28987         var style = this.style;
28988         delete this.style;
28989         
28990         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28991         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28992         this.viewEl = this.wrap.createChild({ tag: 'div' });
28993         if (style) {
28994             this.viewEl.applyStyles(style);
28995         }
28996         if (this.width) {
28997             this.viewEl.setWidth(this.width);
28998         }
28999         if (this.height) {
29000             this.viewEl.setHeight(this.height);
29001         }
29002         //if(this.inputValue !== undefined){
29003         //this.setValue(this.value);
29004         
29005         
29006         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
29007         
29008         
29009         this.grid.render();
29010         this.grid.getDataSource().on('remove', this.refreshValue, this);
29011         this.grid.getDataSource().on('update', this.refreshValue, this);
29012         this.grid.on('afteredit', this.refreshValue, this);
29013  
29014     },
29015      
29016     
29017     /**
29018      * Sets the value of the item. 
29019      * @param {String} either an object  or a string..
29020      */
29021     setValue : function(v){
29022         //this.value = v;
29023         v = v || []; // empty set..
29024         // this does not seem smart - it really only affects memoryproxy grids..
29025         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
29026             var ds = this.grid.getDataSource();
29027             // assumes a json reader..
29028             var data = {}
29029             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
29030             ds.loadData( data);
29031         }
29032         // clear selection so it does not get stale.
29033         if (this.grid.sm) { 
29034             this.grid.sm.clearSelections();
29035         }
29036         
29037         Roo.form.GridField.superclass.setValue.call(this, v);
29038         this.refreshValue();
29039         // should load data in the grid really....
29040     },
29041     
29042     // private
29043     refreshValue: function() {
29044          var val = [];
29045         this.grid.getDataSource().each(function(r) {
29046             val.push(r.data);
29047         });
29048         this.el.dom.value = Roo.encode(val);
29049     }
29050     
29051      
29052     
29053     
29054 });/*
29055  * Based on:
29056  * Ext JS Library 1.1.1
29057  * Copyright(c) 2006-2007, Ext JS, LLC.
29058  *
29059  * Originally Released Under LGPL - original licence link has changed is not relivant.
29060  *
29061  * Fork - LGPL
29062  * <script type="text/javascript">
29063  */
29064 /**
29065  * @class Roo.form.DisplayField
29066  * @extends Roo.form.Field
29067  * A generic Field to display non-editable data.
29068  * @constructor
29069  * Creates a new Display Field item.
29070  * @param {Object} config Configuration options
29071  */
29072 Roo.form.DisplayField = function(config){
29073     Roo.form.DisplayField.superclass.constructor.call(this, config);
29074     
29075 };
29076
29077 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
29078     inputType:      'hidden',
29079     allowBlank:     true,
29080     readOnly:         true,
29081     
29082  
29083     /**
29084      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29085      */
29086     focusClass : undefined,
29087     /**
29088      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29089      */
29090     fieldClass: 'x-form-field',
29091     
29092      /**
29093      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
29094      */
29095     valueRenderer: undefined,
29096     
29097     width: 100,
29098     /**
29099      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29100      * {tag: "input", type: "checkbox", autocomplete: "off"})
29101      */
29102      
29103  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29104
29105     onResize : function(){
29106         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
29107         
29108     },
29109
29110     initEvents : function(){
29111         // Roo.form.Checkbox.superclass.initEvents.call(this);
29112         // has no events...
29113        
29114     },
29115
29116
29117     getResizeEl : function(){
29118         return this.wrap;
29119     },
29120
29121     getPositionEl : function(){
29122         return this.wrap;
29123     },
29124
29125     // private
29126     onRender : function(ct, position){
29127         
29128         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
29129         //if(this.inputValue !== undefined){
29130         this.wrap = this.el.wrap();
29131         
29132         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
29133         
29134         if (this.bodyStyle) {
29135             this.viewEl.applyStyles(this.bodyStyle);
29136         }
29137         //this.viewEl.setStyle('padding', '2px');
29138         
29139         this.setValue(this.value);
29140         
29141     },
29142 /*
29143     // private
29144     initValue : Roo.emptyFn,
29145
29146   */
29147
29148         // private
29149     onClick : function(){
29150         
29151     },
29152
29153     /**
29154      * Sets the checked state of the checkbox.
29155      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
29156      */
29157     setValue : function(v){
29158         this.value = v;
29159         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
29160         // this might be called before we have a dom element..
29161         if (!this.viewEl) {
29162             return;
29163         }
29164         this.viewEl.dom.innerHTML = html;
29165         Roo.form.DisplayField.superclass.setValue.call(this, v);
29166
29167     }
29168 });/*
29169  * 
29170  * Licence- LGPL
29171  * 
29172  */
29173
29174 /**
29175  * @class Roo.form.DayPicker
29176  * @extends Roo.form.Field
29177  * A Day picker show [M] [T] [W] ....
29178  * @constructor
29179  * Creates a new Day Picker
29180  * @param {Object} config Configuration options
29181  */
29182 Roo.form.DayPicker= function(config){
29183     Roo.form.DayPicker.superclass.constructor.call(this, config);
29184      
29185 };
29186
29187 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
29188     /**
29189      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29190      */
29191     focusClass : undefined,
29192     /**
29193      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29194      */
29195     fieldClass: "x-form-field",
29196    
29197     /**
29198      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29199      * {tag: "input", type: "checkbox", autocomplete: "off"})
29200      */
29201     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
29202     
29203    
29204     actionMode : 'viewEl', 
29205     //
29206     // private
29207  
29208     inputType : 'hidden',
29209     
29210      
29211     inputElement: false, // real input element?
29212     basedOn: false, // ????
29213     
29214     isFormField: true, // not sure where this is needed!!!!
29215
29216     onResize : function(){
29217         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
29218         if(!this.boxLabel){
29219             this.el.alignTo(this.wrap, 'c-c');
29220         }
29221     },
29222
29223     initEvents : function(){
29224         Roo.form.Checkbox.superclass.initEvents.call(this);
29225         this.el.on("click", this.onClick,  this);
29226         this.el.on("change", this.onClick,  this);
29227     },
29228
29229
29230     getResizeEl : function(){
29231         return this.wrap;
29232     },
29233
29234     getPositionEl : function(){
29235         return this.wrap;
29236     },
29237
29238     
29239     // private
29240     onRender : function(ct, position){
29241         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
29242        
29243         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
29244         
29245         var r1 = '<table><tr>';
29246         var r2 = '<tr class="x-form-daypick-icons">';
29247         for (var i=0; i < 7; i++) {
29248             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
29249             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
29250         }
29251         
29252         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
29253         viewEl.select('img').on('click', this.onClick, this);
29254         this.viewEl = viewEl;   
29255         
29256         
29257         // this will not work on Chrome!!!
29258         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
29259         this.el.on('propertychange', this.setFromHidden,  this);  //ie
29260         
29261         
29262           
29263
29264     },
29265
29266     // private
29267     initValue : Roo.emptyFn,
29268
29269     /**
29270      * Returns the checked state of the checkbox.
29271      * @return {Boolean} True if checked, else false
29272      */
29273     getValue : function(){
29274         return this.el.dom.value;
29275         
29276     },
29277
29278         // private
29279     onClick : function(e){ 
29280         //this.setChecked(!this.checked);
29281         Roo.get(e.target).toggleClass('x-menu-item-checked');
29282         this.refreshValue();
29283         //if(this.el.dom.checked != this.checked){
29284         //    this.setValue(this.el.dom.checked);
29285        // }
29286     },
29287     
29288     // private
29289     refreshValue : function()
29290     {
29291         var val = '';
29292         this.viewEl.select('img',true).each(function(e,i,n)  {
29293             val += e.is(".x-menu-item-checked") ? String(n) : '';
29294         });
29295         this.setValue(val, true);
29296     },
29297
29298     /**
29299      * Sets the checked state of the checkbox.
29300      * On is always based on a string comparison between inputValue and the param.
29301      * @param {Boolean/String} value - the value to set 
29302      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
29303      */
29304     setValue : function(v,suppressEvent){
29305         if (!this.el.dom) {
29306             return;
29307         }
29308         var old = this.el.dom.value ;
29309         this.el.dom.value = v;
29310         if (suppressEvent) {
29311             return ;
29312         }
29313          
29314         // update display..
29315         this.viewEl.select('img',true).each(function(e,i,n)  {
29316             
29317             var on = e.is(".x-menu-item-checked");
29318             var newv = v.indexOf(String(n)) > -1;
29319             if (on != newv) {
29320                 e.toggleClass('x-menu-item-checked');
29321             }
29322             
29323         });
29324         
29325         
29326         this.fireEvent('change', this, v, old);
29327         
29328         
29329     },
29330    
29331     // handle setting of hidden value by some other method!!?!?
29332     setFromHidden: function()
29333     {
29334         if(!this.el){
29335             return;
29336         }
29337         //console.log("SET FROM HIDDEN");
29338         //alert('setFrom hidden');
29339         this.setValue(this.el.dom.value);
29340     },
29341     
29342     onDestroy : function()
29343     {
29344         if(this.viewEl){
29345             Roo.get(this.viewEl).remove();
29346         }
29347          
29348         Roo.form.DayPicker.superclass.onDestroy.call(this);
29349     }
29350
29351 });/*
29352  * RooJS Library 1.1.1
29353  * Copyright(c) 2008-2011  Alan Knowles
29354  *
29355  * License - LGPL
29356  */
29357  
29358
29359 /**
29360  * @class Roo.form.ComboCheck
29361  * @extends Roo.form.ComboBox
29362  * A combobox for multiple select items.
29363  *
29364  * FIXME - could do with a reset button..
29365  * 
29366  * @constructor
29367  * Create a new ComboCheck
29368  * @param {Object} config Configuration options
29369  */
29370 Roo.form.ComboCheck = function(config){
29371     Roo.form.ComboCheck.superclass.constructor.call(this, config);
29372     // should verify some data...
29373     // like
29374     // hiddenName = required..
29375     // displayField = required
29376     // valudField == required
29377     var req= [ 'hiddenName', 'displayField', 'valueField' ];
29378     var _t = this;
29379     Roo.each(req, function(e) {
29380         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
29381             throw "Roo.form.ComboCheck : missing value for: " + e;
29382         }
29383     });
29384     
29385     
29386 };
29387
29388 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
29389      
29390      
29391     editable : false,
29392      
29393     selectedClass: 'x-menu-item-checked', 
29394     
29395     // private
29396     onRender : function(ct, position){
29397         var _t = this;
29398         
29399         
29400         
29401         if(!this.tpl){
29402             var cls = 'x-combo-list';
29403
29404             
29405             this.tpl =  new Roo.Template({
29406                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
29407                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
29408                    '<span>{' + this.displayField + '}</span>' +
29409                     '</div>' 
29410                 
29411             });
29412         }
29413  
29414         
29415         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
29416         this.view.singleSelect = false;
29417         this.view.multiSelect = true;
29418         this.view.toggleSelect = true;
29419         this.pageTb.add(new Roo.Toolbar.Fill(), {
29420             
29421             text: 'Done',
29422             handler: function()
29423             {
29424                 _t.collapse();
29425             }
29426         });
29427     },
29428     
29429     onViewOver : function(e, t){
29430         // do nothing...
29431         return;
29432         
29433     },
29434     
29435     onViewClick : function(doFocus,index){
29436         return;
29437         
29438     },
29439     select: function () {
29440         //Roo.log("SELECT CALLED");
29441     },
29442      
29443     selectByValue : function(xv, scrollIntoView){
29444         var ar = this.getValueArray();
29445         var sels = [];
29446         
29447         Roo.each(ar, function(v) {
29448             if(v === undefined || v === null){
29449                 return;
29450             }
29451             var r = this.findRecord(this.valueField, v);
29452             if(r){
29453                 sels.push(this.store.indexOf(r))
29454                 
29455             }
29456         },this);
29457         this.view.select(sels);
29458         return false;
29459     },
29460     
29461     
29462     
29463     onSelect : function(record, index){
29464        // Roo.log("onselect Called");
29465        // this is only called by the clear button now..
29466         this.view.clearSelections();
29467         this.setValue('[]');
29468         if (this.value != this.valueBefore) {
29469             this.fireEvent('change', this, this.value, this.valueBefore);
29470         }
29471     },
29472     getValueArray : function()
29473     {
29474         var ar = [] ;
29475         
29476         try {
29477             //Roo.log(this.value);
29478             if (typeof(this.value) == 'undefined') {
29479                 return [];
29480             }
29481             var ar = Roo.decode(this.value);
29482             return  ar instanceof Array ? ar : []; //?? valid?
29483             
29484         } catch(e) {
29485             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
29486             return [];
29487         }
29488          
29489     },
29490     expand : function ()
29491     {
29492         Roo.form.ComboCheck.superclass.expand.call(this);
29493         this.valueBefore = this.value;
29494         
29495
29496     },
29497     
29498     collapse : function(){
29499         Roo.form.ComboCheck.superclass.collapse.call(this);
29500         var sl = this.view.getSelectedIndexes();
29501         var st = this.store;
29502         var nv = [];
29503         var tv = [];
29504         var r;
29505         Roo.each(sl, function(i) {
29506             r = st.getAt(i);
29507             nv.push(r.get(this.valueField));
29508         },this);
29509         this.setValue(Roo.encode(nv));
29510         if (this.value != this.valueBefore) {
29511
29512             this.fireEvent('change', this, this.value, this.valueBefore);
29513         }
29514         
29515     },
29516     
29517     setValue : function(v){
29518         // Roo.log(v);
29519         this.value = v;
29520         
29521         var vals = this.getValueArray();
29522         var tv = [];
29523         Roo.each(vals, function(k) {
29524             var r = this.findRecord(this.valueField, k);
29525             if(r){
29526                 tv.push(r.data[this.displayField]);
29527             }else if(this.valueNotFoundText !== undefined){
29528                 tv.push( this.valueNotFoundText );
29529             }
29530         },this);
29531        // Roo.log(tv);
29532         
29533         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
29534         this.hiddenField.value = v;
29535         this.value = v;
29536     }
29537     
29538 });//<script type="text/javasscript">
29539  
29540
29541 /**
29542  * @class Roo.DDView
29543  * A DnD enabled version of Roo.View.
29544  * @param {Element/String} container The Element in which to create the View.
29545  * @param {String} tpl The template string used to create the markup for each element of the View
29546  * @param {Object} config The configuration properties. These include all the config options of
29547  * {@link Roo.View} plus some specific to this class.<br>
29548  * <p>
29549  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
29550  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
29551  * <p>
29552  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
29553 .x-view-drag-insert-above {
29554         border-top:1px dotted #3366cc;
29555 }
29556 .x-view-drag-insert-below {
29557         border-bottom:1px dotted #3366cc;
29558 }
29559 </code></pre>
29560  * 
29561  */
29562  
29563 Roo.DDView = function(container, tpl, config) {
29564     Roo.DDView.superclass.constructor.apply(this, arguments);
29565     this.getEl().setStyle("outline", "0px none");
29566     this.getEl().unselectable();
29567     if (this.dragGroup) {
29568                 this.setDraggable(this.dragGroup.split(","));
29569     }
29570     if (this.dropGroup) {
29571                 this.setDroppable(this.dropGroup.split(","));
29572     }
29573     if (this.deletable) {
29574         this.setDeletable();
29575     }
29576     this.isDirtyFlag = false;
29577         this.addEvents({
29578                 "drop" : true
29579         });
29580 };
29581
29582 Roo.extend(Roo.DDView, Roo.View, {
29583 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
29584 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
29585 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
29586 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
29587
29588         isFormField: true,
29589
29590         reset: Roo.emptyFn,
29591         
29592         clearInvalid: Roo.form.Field.prototype.clearInvalid,
29593
29594         validate: function() {
29595                 return true;
29596         },
29597         
29598         destroy: function() {
29599                 this.purgeListeners();
29600                 this.getEl.removeAllListeners();
29601                 this.getEl().remove();
29602                 if (this.dragZone) {
29603                         if (this.dragZone.destroy) {
29604                                 this.dragZone.destroy();
29605                         }
29606                 }
29607                 if (this.dropZone) {
29608                         if (this.dropZone.destroy) {
29609                                 this.dropZone.destroy();
29610                         }
29611                 }
29612         },
29613
29614 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
29615         getName: function() {
29616                 return this.name;
29617         },
29618
29619 /**     Loads the View from a JSON string representing the Records to put into the Store. */
29620         setValue: function(v) {
29621                 if (!this.store) {
29622                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
29623                 }
29624                 var data = {};
29625                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
29626                 this.store.proxy = new Roo.data.MemoryProxy(data);
29627                 this.store.load();
29628         },
29629
29630 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
29631         getValue: function() {
29632                 var result = '(';
29633                 this.store.each(function(rec) {
29634                         result += rec.id + ',';
29635                 });
29636                 return result.substr(0, result.length - 1) + ')';
29637         },
29638         
29639         getIds: function() {
29640                 var i = 0, result = new Array(this.store.getCount());
29641                 this.store.each(function(rec) {
29642                         result[i++] = rec.id;
29643                 });
29644                 return result;
29645         },
29646         
29647         isDirty: function() {
29648                 return this.isDirtyFlag;
29649         },
29650
29651 /**
29652  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
29653  *      whole Element becomes the target, and this causes the drop gesture to append.
29654  */
29655     getTargetFromEvent : function(e) {
29656                 var target = e.getTarget();
29657                 while ((target !== null) && (target.parentNode != this.el.dom)) {
29658                 target = target.parentNode;
29659                 }
29660                 if (!target) {
29661                         target = this.el.dom.lastChild || this.el.dom;
29662                 }
29663                 return target;
29664     },
29665
29666 /**
29667  *      Create the drag data which consists of an object which has the property "ddel" as
29668  *      the drag proxy element. 
29669  */
29670     getDragData : function(e) {
29671         var target = this.findItemFromChild(e.getTarget());
29672                 if(target) {
29673                         this.handleSelection(e);
29674                         var selNodes = this.getSelectedNodes();
29675             var dragData = {
29676                 source: this,
29677                 copy: this.copy || (this.allowCopy && e.ctrlKey),
29678                 nodes: selNodes,
29679                 records: []
29680                         };
29681                         var selectedIndices = this.getSelectedIndexes();
29682                         for (var i = 0; i < selectedIndices.length; i++) {
29683                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
29684                         }
29685                         if (selNodes.length == 1) {
29686                                 dragData.ddel = target.cloneNode(true); // the div element
29687                         } else {
29688                                 var div = document.createElement('div'); // create the multi element drag "ghost"
29689                                 div.className = 'multi-proxy';
29690                                 for (var i = 0, len = selNodes.length; i < len; i++) {
29691                                         div.appendChild(selNodes[i].cloneNode(true));
29692                                 }
29693                                 dragData.ddel = div;
29694                         }
29695             //console.log(dragData)
29696             //console.log(dragData.ddel.innerHTML)
29697                         return dragData;
29698                 }
29699         //console.log('nodragData')
29700                 return false;
29701     },
29702     
29703 /**     Specify to which ddGroup items in this DDView may be dragged. */
29704     setDraggable: function(ddGroup) {
29705         if (ddGroup instanceof Array) {
29706                 Roo.each(ddGroup, this.setDraggable, this);
29707                 return;
29708         }
29709         if (this.dragZone) {
29710                 this.dragZone.addToGroup(ddGroup);
29711         } else {
29712                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
29713                                 containerScroll: true,
29714                                 ddGroup: ddGroup 
29715
29716                         });
29717 //                      Draggability implies selection. DragZone's mousedown selects the element.
29718                         if (!this.multiSelect) { this.singleSelect = true; }
29719
29720 //                      Wire the DragZone's handlers up to methods in *this*
29721                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
29722                 }
29723     },
29724
29725 /**     Specify from which ddGroup this DDView accepts drops. */
29726     setDroppable: function(ddGroup) {
29727         if (ddGroup instanceof Array) {
29728                 Roo.each(ddGroup, this.setDroppable, this);
29729                 return;
29730         }
29731         if (this.dropZone) {
29732                 this.dropZone.addToGroup(ddGroup);
29733         } else {
29734                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
29735                                 containerScroll: true,
29736                                 ddGroup: ddGroup
29737                         });
29738
29739 //                      Wire the DropZone's handlers up to methods in *this*
29740                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
29741                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
29742                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
29743                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
29744                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
29745                 }
29746     },
29747
29748 /**     Decide whether to drop above or below a View node. */
29749     getDropPoint : function(e, n, dd){
29750         if (n == this.el.dom) { return "above"; }
29751                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
29752                 var c = t + (b - t) / 2;
29753                 var y = Roo.lib.Event.getPageY(e);
29754                 if(y <= c) {
29755                         return "above";
29756                 }else{
29757                         return "below";
29758                 }
29759     },
29760
29761     onNodeEnter : function(n, dd, e, data){
29762                 return false;
29763     },
29764     
29765     onNodeOver : function(n, dd, e, data){
29766                 var pt = this.getDropPoint(e, n, dd);
29767                 // set the insert point style on the target node
29768                 var dragElClass = this.dropNotAllowed;
29769                 if (pt) {
29770                         var targetElClass;
29771                         if (pt == "above"){
29772                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
29773                                 targetElClass = "x-view-drag-insert-above";
29774                         } else {
29775                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
29776                                 targetElClass = "x-view-drag-insert-below";
29777                         }
29778                         if (this.lastInsertClass != targetElClass){
29779                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
29780                                 this.lastInsertClass = targetElClass;
29781                         }
29782                 }
29783                 return dragElClass;
29784         },
29785
29786     onNodeOut : function(n, dd, e, data){
29787                 this.removeDropIndicators(n);
29788     },
29789
29790     onNodeDrop : function(n, dd, e, data){
29791         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
29792                 return false;
29793         }
29794         var pt = this.getDropPoint(e, n, dd);
29795                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
29796                 if (pt == "below") { insertAt++; }
29797                 for (var i = 0; i < data.records.length; i++) {
29798                         var r = data.records[i];
29799                         var dup = this.store.getById(r.id);
29800                         if (dup && (dd != this.dragZone)) {
29801                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
29802                         } else {
29803                                 if (data.copy) {
29804                                         this.store.insert(insertAt++, r.copy());
29805                                 } else {
29806                                         data.source.isDirtyFlag = true;
29807                                         r.store.remove(r);
29808                                         this.store.insert(insertAt++, r);
29809                                 }
29810                                 this.isDirtyFlag = true;
29811                         }
29812                 }
29813                 this.dragZone.cachedTarget = null;
29814                 return true;
29815     },
29816
29817     removeDropIndicators : function(n){
29818                 if(n){
29819                         Roo.fly(n).removeClass([
29820                                 "x-view-drag-insert-above",
29821                                 "x-view-drag-insert-below"]);
29822                         this.lastInsertClass = "_noclass";
29823                 }
29824     },
29825
29826 /**
29827  *      Utility method. Add a delete option to the DDView's context menu.
29828  *      @param {String} imageUrl The URL of the "delete" icon image.
29829  */
29830         setDeletable: function(imageUrl) {
29831                 if (!this.singleSelect && !this.multiSelect) {
29832                         this.singleSelect = true;
29833                 }
29834                 var c = this.getContextMenu();
29835                 this.contextMenu.on("itemclick", function(item) {
29836                         switch (item.id) {
29837                                 case "delete":
29838                                         this.remove(this.getSelectedIndexes());
29839                                         break;
29840                         }
29841                 }, this);
29842                 this.contextMenu.add({
29843                         icon: imageUrl,
29844                         id: "delete",
29845                         text: 'Delete'
29846                 });
29847         },
29848         
29849 /**     Return the context menu for this DDView. */
29850         getContextMenu: function() {
29851                 if (!this.contextMenu) {
29852 //                      Create the View's context menu
29853                         this.contextMenu = new Roo.menu.Menu({
29854                                 id: this.id + "-contextmenu"
29855                         });
29856                         this.el.on("contextmenu", this.showContextMenu, this);
29857                 }
29858                 return this.contextMenu;
29859         },
29860         
29861         disableContextMenu: function() {
29862                 if (this.contextMenu) {
29863                         this.el.un("contextmenu", this.showContextMenu, this);
29864                 }
29865         },
29866
29867         showContextMenu: function(e, item) {
29868         item = this.findItemFromChild(e.getTarget());
29869                 if (item) {
29870                         e.stopEvent();
29871                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
29872                         this.contextMenu.showAt(e.getXY());
29873             }
29874     },
29875
29876 /**
29877  *      Remove {@link Roo.data.Record}s at the specified indices.
29878  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
29879  */
29880     remove: function(selectedIndices) {
29881                 selectedIndices = [].concat(selectedIndices);
29882                 for (var i = 0; i < selectedIndices.length; i++) {
29883                         var rec = this.store.getAt(selectedIndices[i]);
29884                         this.store.remove(rec);
29885                 }
29886     },
29887
29888 /**
29889  *      Double click fires the event, but also, if this is draggable, and there is only one other
29890  *      related DropZone, it transfers the selected node.
29891  */
29892     onDblClick : function(e){
29893         var item = this.findItemFromChild(e.getTarget());
29894         if(item){
29895             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
29896                 return false;
29897             }
29898             if (this.dragGroup) {
29899                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
29900                     while (targets.indexOf(this.dropZone) > -1) {
29901                             targets.remove(this.dropZone);
29902                                 }
29903                     if (targets.length == 1) {
29904                                         this.dragZone.cachedTarget = null;
29905                         var el = Roo.get(targets[0].getEl());
29906                         var box = el.getBox(true);
29907                         targets[0].onNodeDrop(el.dom, {
29908                                 target: el.dom,
29909                                 xy: [box.x, box.y + box.height - 1]
29910                         }, null, this.getDragData(e));
29911                     }
29912                 }
29913         }
29914     },
29915     
29916     handleSelection: function(e) {
29917                 this.dragZone.cachedTarget = null;
29918         var item = this.findItemFromChild(e.getTarget());
29919         if (!item) {
29920                 this.clearSelections(true);
29921                 return;
29922         }
29923                 if (item && (this.multiSelect || this.singleSelect)){
29924                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
29925                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
29926                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
29927                                 this.unselect(item);
29928                         } else {
29929                                 this.select(item, this.multiSelect && e.ctrlKey);
29930                                 this.lastSelection = item;
29931                         }
29932                 }
29933     },
29934
29935     onItemClick : function(item, index, e){
29936                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
29937                         return false;
29938                 }
29939                 return true;
29940     },
29941
29942     unselect : function(nodeInfo, suppressEvent){
29943                 var node = this.getNode(nodeInfo);
29944                 if(node && this.isSelected(node)){
29945                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
29946                                 Roo.fly(node).removeClass(this.selectedClass);
29947                                 this.selections.remove(node);
29948                                 if(!suppressEvent){
29949                                         this.fireEvent("selectionchange", this, this.selections);
29950                                 }
29951                         }
29952                 }
29953     }
29954 });
29955 /*
29956  * Based on:
29957  * Ext JS Library 1.1.1
29958  * Copyright(c) 2006-2007, Ext JS, LLC.
29959  *
29960  * Originally Released Under LGPL - original licence link has changed is not relivant.
29961  *
29962  * Fork - LGPL
29963  * <script type="text/javascript">
29964  */
29965  
29966 /**
29967  * @class Roo.LayoutManager
29968  * @extends Roo.util.Observable
29969  * Base class for layout managers.
29970  */
29971 Roo.LayoutManager = function(container, config){
29972     Roo.LayoutManager.superclass.constructor.call(this);
29973     this.el = Roo.get(container);
29974     // ie scrollbar fix
29975     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
29976         document.body.scroll = "no";
29977     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
29978         this.el.position('relative');
29979     }
29980     this.id = this.el.id;
29981     this.el.addClass("x-layout-container");
29982     /** false to disable window resize monitoring @type Boolean */
29983     this.monitorWindowResize = true;
29984     this.regions = {};
29985     this.addEvents({
29986         /**
29987          * @event layout
29988          * Fires when a layout is performed. 
29989          * @param {Roo.LayoutManager} this
29990          */
29991         "layout" : true,
29992         /**
29993          * @event regionresized
29994          * Fires when the user resizes a region. 
29995          * @param {Roo.LayoutRegion} region The resized region
29996          * @param {Number} newSize The new size (width for east/west, height for north/south)
29997          */
29998         "regionresized" : true,
29999         /**
30000          * @event regioncollapsed
30001          * Fires when a region is collapsed. 
30002          * @param {Roo.LayoutRegion} region The collapsed region
30003          */
30004         "regioncollapsed" : true,
30005         /**
30006          * @event regionexpanded
30007          * Fires when a region is expanded.  
30008          * @param {Roo.LayoutRegion} region The expanded region
30009          */
30010         "regionexpanded" : true
30011     });
30012     this.updating = false;
30013     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
30014 };
30015
30016 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
30017     /**
30018      * Returns true if this layout is currently being updated
30019      * @return {Boolean}
30020      */
30021     isUpdating : function(){
30022         return this.updating; 
30023     },
30024     
30025     /**
30026      * Suspend the LayoutManager from doing auto-layouts while
30027      * making multiple add or remove calls
30028      */
30029     beginUpdate : function(){
30030         this.updating = true;    
30031     },
30032     
30033     /**
30034      * Restore auto-layouts and optionally disable the manager from performing a layout
30035      * @param {Boolean} noLayout true to disable a layout update 
30036      */
30037     endUpdate : function(noLayout){
30038         this.updating = false;
30039         if(!noLayout){
30040             this.layout();
30041         }    
30042     },
30043     
30044     layout: function(){
30045         
30046     },
30047     
30048     onRegionResized : function(region, newSize){
30049         this.fireEvent("regionresized", region, newSize);
30050         this.layout();
30051     },
30052     
30053     onRegionCollapsed : function(region){
30054         this.fireEvent("regioncollapsed", region);
30055     },
30056     
30057     onRegionExpanded : function(region){
30058         this.fireEvent("regionexpanded", region);
30059     },
30060         
30061     /**
30062      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
30063      * performs box-model adjustments.
30064      * @return {Object} The size as an object {width: (the width), height: (the height)}
30065      */
30066     getViewSize : function(){
30067         var size;
30068         if(this.el.dom != document.body){
30069             size = this.el.getSize();
30070         }else{
30071             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
30072         }
30073         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
30074         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30075         return size;
30076     },
30077     
30078     /**
30079      * Returns the Element this layout is bound to.
30080      * @return {Roo.Element}
30081      */
30082     getEl : function(){
30083         return this.el;
30084     },
30085     
30086     /**
30087      * Returns the specified region.
30088      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
30089      * @return {Roo.LayoutRegion}
30090      */
30091     getRegion : function(target){
30092         return this.regions[target.toLowerCase()];
30093     },
30094     
30095     onWindowResize : function(){
30096         if(this.monitorWindowResize){
30097             this.layout();
30098         }
30099     }
30100 });/*
30101  * Based on:
30102  * Ext JS Library 1.1.1
30103  * Copyright(c) 2006-2007, Ext JS, LLC.
30104  *
30105  * Originally Released Under LGPL - original licence link has changed is not relivant.
30106  *
30107  * Fork - LGPL
30108  * <script type="text/javascript">
30109  */
30110 /**
30111  * @class Roo.BorderLayout
30112  * @extends Roo.LayoutManager
30113  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
30114  * please see: <br><br>
30115  * <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>
30116  * <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>
30117  * Example:
30118  <pre><code>
30119  var layout = new Roo.BorderLayout(document.body, {
30120     north: {
30121         initialSize: 25,
30122         titlebar: false
30123     },
30124     west: {
30125         split:true,
30126         initialSize: 200,
30127         minSize: 175,
30128         maxSize: 400,
30129         titlebar: true,
30130         collapsible: true
30131     },
30132     east: {
30133         split:true,
30134         initialSize: 202,
30135         minSize: 175,
30136         maxSize: 400,
30137         titlebar: true,
30138         collapsible: true
30139     },
30140     south: {
30141         split:true,
30142         initialSize: 100,
30143         minSize: 100,
30144         maxSize: 200,
30145         titlebar: true,
30146         collapsible: true
30147     },
30148     center: {
30149         titlebar: true,
30150         autoScroll:true,
30151         resizeTabs: true,
30152         minTabWidth: 50,
30153         preferredTabWidth: 150
30154     }
30155 });
30156
30157 // shorthand
30158 var CP = Roo.ContentPanel;
30159
30160 layout.beginUpdate();
30161 layout.add("north", new CP("north", "North"));
30162 layout.add("south", new CP("south", {title: "South", closable: true}));
30163 layout.add("west", new CP("west", {title: "West"}));
30164 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
30165 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
30166 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
30167 layout.getRegion("center").showPanel("center1");
30168 layout.endUpdate();
30169 </code></pre>
30170
30171 <b>The container the layout is rendered into can be either the body element or any other element.
30172 If it is not the body element, the container needs to either be an absolute positioned element,
30173 or you will need to add "position:relative" to the css of the container.  You will also need to specify
30174 the container size if it is not the body element.</b>
30175
30176 * @constructor
30177 * Create a new BorderLayout
30178 * @param {String/HTMLElement/Element} container The container this layout is bound to
30179 * @param {Object} config Configuration options
30180  */
30181 Roo.BorderLayout = function(container, config){
30182     config = config || {};
30183     Roo.BorderLayout.superclass.constructor.call(this, container, config);
30184     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
30185     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
30186         var target = this.factory.validRegions[i];
30187         if(config[target]){
30188             this.addRegion(target, config[target]);
30189         }
30190     }
30191 };
30192
30193 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
30194     /**
30195      * Creates and adds a new region if it doesn't already exist.
30196      * @param {String} target The target region key (north, south, east, west or center).
30197      * @param {Object} config The regions config object
30198      * @return {BorderLayoutRegion} The new region
30199      */
30200     addRegion : function(target, config){
30201         if(!this.regions[target]){
30202             var r = this.factory.create(target, this, config);
30203             this.bindRegion(target, r);
30204         }
30205         return this.regions[target];
30206     },
30207
30208     // private (kinda)
30209     bindRegion : function(name, r){
30210         this.regions[name] = r;
30211         r.on("visibilitychange", this.layout, this);
30212         r.on("paneladded", this.layout, this);
30213         r.on("panelremoved", this.layout, this);
30214         r.on("invalidated", this.layout, this);
30215         r.on("resized", this.onRegionResized, this);
30216         r.on("collapsed", this.onRegionCollapsed, this);
30217         r.on("expanded", this.onRegionExpanded, this);
30218     },
30219
30220     /**
30221      * Performs a layout update.
30222      */
30223     layout : function(){
30224         if(this.updating) return;
30225         var size = this.getViewSize();
30226         var w = size.width;
30227         var h = size.height;
30228         var centerW = w;
30229         var centerH = h;
30230         var centerY = 0;
30231         var centerX = 0;
30232         //var x = 0, y = 0;
30233
30234         var rs = this.regions;
30235         var north = rs["north"];
30236         var south = rs["south"]; 
30237         var west = rs["west"];
30238         var east = rs["east"];
30239         var center = rs["center"];
30240         //if(this.hideOnLayout){ // not supported anymore
30241             //c.el.setStyle("display", "none");
30242         //}
30243         if(north && north.isVisible()){
30244             var b = north.getBox();
30245             var m = north.getMargins();
30246             b.width = w - (m.left+m.right);
30247             b.x = m.left;
30248             b.y = m.top;
30249             centerY = b.height + b.y + m.bottom;
30250             centerH -= centerY;
30251             north.updateBox(this.safeBox(b));
30252         }
30253         if(south && south.isVisible()){
30254             var b = south.getBox();
30255             var m = south.getMargins();
30256             b.width = w - (m.left+m.right);
30257             b.x = m.left;
30258             var totalHeight = (b.height + m.top + m.bottom);
30259             b.y = h - totalHeight + m.top;
30260             centerH -= totalHeight;
30261             south.updateBox(this.safeBox(b));
30262         }
30263         if(west && west.isVisible()){
30264             var b = west.getBox();
30265             var m = west.getMargins();
30266             b.height = centerH - (m.top+m.bottom);
30267             b.x = m.left;
30268             b.y = centerY + m.top;
30269             var totalWidth = (b.width + m.left + m.right);
30270             centerX += totalWidth;
30271             centerW -= totalWidth;
30272             west.updateBox(this.safeBox(b));
30273         }
30274         if(east && east.isVisible()){
30275             var b = east.getBox();
30276             var m = east.getMargins();
30277             b.height = centerH - (m.top+m.bottom);
30278             var totalWidth = (b.width + m.left + m.right);
30279             b.x = w - totalWidth + m.left;
30280             b.y = centerY + m.top;
30281             centerW -= totalWidth;
30282             east.updateBox(this.safeBox(b));
30283         }
30284         if(center){
30285             var m = center.getMargins();
30286             var centerBox = {
30287                 x: centerX + m.left,
30288                 y: centerY + m.top,
30289                 width: centerW - (m.left+m.right),
30290                 height: centerH - (m.top+m.bottom)
30291             };
30292             //if(this.hideOnLayout){
30293                 //center.el.setStyle("display", "block");
30294             //}
30295             center.updateBox(this.safeBox(centerBox));
30296         }
30297         this.el.repaint();
30298         this.fireEvent("layout", this);
30299     },
30300
30301     // private
30302     safeBox : function(box){
30303         box.width = Math.max(0, box.width);
30304         box.height = Math.max(0, box.height);
30305         return box;
30306     },
30307
30308     /**
30309      * Adds a ContentPanel (or subclass) to this layout.
30310      * @param {String} target The target region key (north, south, east, west or center).
30311      * @param {Roo.ContentPanel} panel The panel to add
30312      * @return {Roo.ContentPanel} The added panel
30313      */
30314     add : function(target, panel){
30315          
30316         target = target.toLowerCase();
30317         return this.regions[target].add(panel);
30318     },
30319
30320     /**
30321      * Remove a ContentPanel (or subclass) to this layout.
30322      * @param {String} target The target region key (north, south, east, west or center).
30323      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
30324      * @return {Roo.ContentPanel} The removed panel
30325      */
30326     remove : function(target, panel){
30327         target = target.toLowerCase();
30328         return this.regions[target].remove(panel);
30329     },
30330
30331     /**
30332      * Searches all regions for a panel with the specified id
30333      * @param {String} panelId
30334      * @return {Roo.ContentPanel} The panel or null if it wasn't found
30335      */
30336     findPanel : function(panelId){
30337         var rs = this.regions;
30338         for(var target in rs){
30339             if(typeof rs[target] != "function"){
30340                 var p = rs[target].getPanel(panelId);
30341                 if(p){
30342                     return p;
30343                 }
30344             }
30345         }
30346         return null;
30347     },
30348
30349     /**
30350      * Searches all regions for a panel with the specified id and activates (shows) it.
30351      * @param {String/ContentPanel} panelId The panels id or the panel itself
30352      * @return {Roo.ContentPanel} The shown panel or null
30353      */
30354     showPanel : function(panelId) {
30355       var rs = this.regions;
30356       for(var target in rs){
30357          var r = rs[target];
30358          if(typeof r != "function"){
30359             if(r.hasPanel(panelId)){
30360                return r.showPanel(panelId);
30361             }
30362          }
30363       }
30364       return null;
30365    },
30366
30367    /**
30368      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
30369      * @param {Roo.state.Provider} provider (optional) An alternate state provider
30370      */
30371     restoreState : function(provider){
30372         if(!provider){
30373             provider = Roo.state.Manager;
30374         }
30375         var sm = new Roo.LayoutStateManager();
30376         sm.init(this, provider);
30377     },
30378
30379     /**
30380      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
30381      * object should contain properties for each region to add ContentPanels to, and each property's value should be
30382      * a valid ContentPanel config object.  Example:
30383      * <pre><code>
30384 // Create the main layout
30385 var layout = new Roo.BorderLayout('main-ct', {
30386     west: {
30387         split:true,
30388         minSize: 175,
30389         titlebar: true
30390     },
30391     center: {
30392         title:'Components'
30393     }
30394 }, 'main-ct');
30395
30396 // Create and add multiple ContentPanels at once via configs
30397 layout.batchAdd({
30398    west: {
30399        id: 'source-files',
30400        autoCreate:true,
30401        title:'Ext Source Files',
30402        autoScroll:true,
30403        fitToFrame:true
30404    },
30405    center : {
30406        el: cview,
30407        autoScroll:true,
30408        fitToFrame:true,
30409        toolbar: tb,
30410        resizeEl:'cbody'
30411    }
30412 });
30413 </code></pre>
30414      * @param {Object} regions An object containing ContentPanel configs by region name
30415      */
30416     batchAdd : function(regions){
30417         this.beginUpdate();
30418         for(var rname in regions){
30419             var lr = this.regions[rname];
30420             if(lr){
30421                 this.addTypedPanels(lr, regions[rname]);
30422             }
30423         }
30424         this.endUpdate();
30425     },
30426
30427     // private
30428     addTypedPanels : function(lr, ps){
30429         if(typeof ps == 'string'){
30430             lr.add(new Roo.ContentPanel(ps));
30431         }
30432         else if(ps instanceof Array){
30433             for(var i =0, len = ps.length; i < len; i++){
30434                 this.addTypedPanels(lr, ps[i]);
30435             }
30436         }
30437         else if(!ps.events){ // raw config?
30438             var el = ps.el;
30439             delete ps.el; // prevent conflict
30440             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
30441         }
30442         else {  // panel object assumed!
30443             lr.add(ps);
30444         }
30445     },
30446     /**
30447      * Adds a xtype elements to the layout.
30448      * <pre><code>
30449
30450 layout.addxtype({
30451        xtype : 'ContentPanel',
30452        region: 'west',
30453        items: [ .... ]
30454    }
30455 );
30456
30457 layout.addxtype({
30458         xtype : 'NestedLayoutPanel',
30459         region: 'west',
30460         layout: {
30461            center: { },
30462            west: { }   
30463         },
30464         items : [ ... list of content panels or nested layout panels.. ]
30465    }
30466 );
30467 </code></pre>
30468      * @param {Object} cfg Xtype definition of item to add.
30469      */
30470     addxtype : function(cfg)
30471     {
30472         // basically accepts a pannel...
30473         // can accept a layout region..!?!?
30474         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
30475         
30476         if (!cfg.xtype.match(/Panel$/)) {
30477             return false;
30478         }
30479         var ret = false;
30480         
30481         if (typeof(cfg.region) == 'undefined') {
30482             Roo.log("Failed to add Panel, region was not set");
30483             Roo.log(cfg);
30484             return false;
30485         }
30486         var region = cfg.region;
30487         delete cfg.region;
30488         
30489           
30490         var xitems = [];
30491         if (cfg.items) {
30492             xitems = cfg.items;
30493             delete cfg.items;
30494         }
30495         var nb = false;
30496         
30497         switch(cfg.xtype) 
30498         {
30499             case 'ContentPanel':  // ContentPanel (el, cfg)
30500             case 'ScrollPanel':  // ContentPanel (el, cfg)
30501                 if(cfg.autoCreate) {
30502                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30503                 } else {
30504                     var el = this.el.createChild();
30505                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
30506                 }
30507                 
30508                 this.add(region, ret);
30509                 break;
30510             
30511             
30512             case 'TreePanel': // our new panel!
30513                 cfg.el = this.el.createChild();
30514                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30515                 this.add(region, ret);
30516                 break;
30517             
30518             case 'NestedLayoutPanel': 
30519                 // create a new Layout (which is  a Border Layout...
30520                 var el = this.el.createChild();
30521                 var clayout = cfg.layout;
30522                 delete cfg.layout;
30523                 clayout.items   = clayout.items  || [];
30524                 // replace this exitems with the clayout ones..
30525                 xitems = clayout.items;
30526                  
30527                 
30528                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
30529                     cfg.background = false;
30530                 }
30531                 var layout = new Roo.BorderLayout(el, clayout);
30532                 
30533                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
30534                 //console.log('adding nested layout panel '  + cfg.toSource());
30535                 this.add(region, ret);
30536                 nb = {}; /// find first...
30537                 break;
30538                 
30539             case 'GridPanel': 
30540             
30541                 // needs grid and region
30542                 
30543                 //var el = this.getRegion(region).el.createChild();
30544                 var el = this.el.createChild();
30545                 // create the grid first...
30546                 
30547                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
30548                 delete cfg.grid;
30549                 if (region == 'center' && this.active ) {
30550                     cfg.background = false;
30551                 }
30552                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
30553                 
30554                 this.add(region, ret);
30555                 if (cfg.background) {
30556                     ret.on('activate', function(gp) {
30557                         if (!gp.grid.rendered) {
30558                             gp.grid.render();
30559                         }
30560                     });
30561                 } else {
30562                     grid.render();
30563                 }
30564                 break;
30565            
30566                
30567                 
30568                 
30569             default: 
30570                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
30571                 return null;
30572              // GridPanel (grid, cfg)
30573             
30574         }
30575         this.beginUpdate();
30576         // add children..
30577         var region = '';
30578         var abn = {};
30579         Roo.each(xitems, function(i)  {
30580             region = nb && i.region ? i.region : false;
30581             
30582             var add = ret.addxtype(i);
30583            
30584             if (region) {
30585                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
30586                 if (!i.background) {
30587                     abn[region] = nb[region] ;
30588                 }
30589             }
30590             
30591         });
30592         this.endUpdate();
30593
30594         // make the last non-background panel active..
30595         //if (nb) { Roo.log(abn); }
30596         if (nb) {
30597             
30598             for(var r in abn) {
30599                 region = this.getRegion(r);
30600                 if (region) {
30601                     // tried using nb[r], but it does not work..
30602                      
30603                     region.showPanel(abn[r]);
30604                    
30605                 }
30606             }
30607         }
30608         return ret;
30609         
30610     }
30611 });
30612
30613 /**
30614  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
30615  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
30616  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
30617  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
30618  * <pre><code>
30619 // shorthand
30620 var CP = Roo.ContentPanel;
30621
30622 var layout = Roo.BorderLayout.create({
30623     north: {
30624         initialSize: 25,
30625         titlebar: false,
30626         panels: [new CP("north", "North")]
30627     },
30628     west: {
30629         split:true,
30630         initialSize: 200,
30631         minSize: 175,
30632         maxSize: 400,
30633         titlebar: true,
30634         collapsible: true,
30635         panels: [new CP("west", {title: "West"})]
30636     },
30637     east: {
30638         split:true,
30639         initialSize: 202,
30640         minSize: 175,
30641         maxSize: 400,
30642         titlebar: true,
30643         collapsible: true,
30644         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
30645     },
30646     south: {
30647         split:true,
30648         initialSize: 100,
30649         minSize: 100,
30650         maxSize: 200,
30651         titlebar: true,
30652         collapsible: true,
30653         panels: [new CP("south", {title: "South", closable: true})]
30654     },
30655     center: {
30656         titlebar: true,
30657         autoScroll:true,
30658         resizeTabs: true,
30659         minTabWidth: 50,
30660         preferredTabWidth: 150,
30661         panels: [
30662             new CP("center1", {title: "Close Me", closable: true}),
30663             new CP("center2", {title: "Center Panel", closable: false})
30664         ]
30665     }
30666 }, document.body);
30667
30668 layout.getRegion("center").showPanel("center1");
30669 </code></pre>
30670  * @param config
30671  * @param targetEl
30672  */
30673 Roo.BorderLayout.create = function(config, targetEl){
30674     var layout = new Roo.BorderLayout(targetEl || document.body, config);
30675     layout.beginUpdate();
30676     var regions = Roo.BorderLayout.RegionFactory.validRegions;
30677     for(var j = 0, jlen = regions.length; j < jlen; j++){
30678         var lr = regions[j];
30679         if(layout.regions[lr] && config[lr].panels){
30680             var r = layout.regions[lr];
30681             var ps = config[lr].panels;
30682             layout.addTypedPanels(r, ps);
30683         }
30684     }
30685     layout.endUpdate();
30686     return layout;
30687 };
30688
30689 // private
30690 Roo.BorderLayout.RegionFactory = {
30691     // private
30692     validRegions : ["north","south","east","west","center"],
30693
30694     // private
30695     create : function(target, mgr, config){
30696         target = target.toLowerCase();
30697         if(config.lightweight || config.basic){
30698             return new Roo.BasicLayoutRegion(mgr, config, target);
30699         }
30700         switch(target){
30701             case "north":
30702                 return new Roo.NorthLayoutRegion(mgr, config);
30703             case "south":
30704                 return new Roo.SouthLayoutRegion(mgr, config);
30705             case "east":
30706                 return new Roo.EastLayoutRegion(mgr, config);
30707             case "west":
30708                 return new Roo.WestLayoutRegion(mgr, config);
30709             case "center":
30710                 return new Roo.CenterLayoutRegion(mgr, config);
30711         }
30712         throw 'Layout region "'+target+'" not supported.';
30713     }
30714 };/*
30715  * Based on:
30716  * Ext JS Library 1.1.1
30717  * Copyright(c) 2006-2007, Ext JS, LLC.
30718  *
30719  * Originally Released Under LGPL - original licence link has changed is not relivant.
30720  *
30721  * Fork - LGPL
30722  * <script type="text/javascript">
30723  */
30724  
30725 /**
30726  * @class Roo.BasicLayoutRegion
30727  * @extends Roo.util.Observable
30728  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
30729  * and does not have a titlebar, tabs or any other features. All it does is size and position 
30730  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
30731  */
30732 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
30733     this.mgr = mgr;
30734     this.position  = pos;
30735     this.events = {
30736         /**
30737          * @scope Roo.BasicLayoutRegion
30738          */
30739         
30740         /**
30741          * @event beforeremove
30742          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
30743          * @param {Roo.LayoutRegion} this
30744          * @param {Roo.ContentPanel} panel The panel
30745          * @param {Object} e The cancel event object
30746          */
30747         "beforeremove" : true,
30748         /**
30749          * @event invalidated
30750          * Fires when the layout for this region is changed.
30751          * @param {Roo.LayoutRegion} this
30752          */
30753         "invalidated" : true,
30754         /**
30755          * @event visibilitychange
30756          * Fires when this region is shown or hidden 
30757          * @param {Roo.LayoutRegion} this
30758          * @param {Boolean} visibility true or false
30759          */
30760         "visibilitychange" : true,
30761         /**
30762          * @event paneladded
30763          * Fires when a panel is added. 
30764          * @param {Roo.LayoutRegion} this
30765          * @param {Roo.ContentPanel} panel The panel
30766          */
30767         "paneladded" : true,
30768         /**
30769          * @event panelremoved
30770          * Fires when a panel is removed. 
30771          * @param {Roo.LayoutRegion} this
30772          * @param {Roo.ContentPanel} panel The panel
30773          */
30774         "panelremoved" : true,
30775         /**
30776          * @event collapsed
30777          * Fires when this region is collapsed.
30778          * @param {Roo.LayoutRegion} this
30779          */
30780         "collapsed" : true,
30781         /**
30782          * @event expanded
30783          * Fires when this region is expanded.
30784          * @param {Roo.LayoutRegion} this
30785          */
30786         "expanded" : true,
30787         /**
30788          * @event slideshow
30789          * Fires when this region is slid into view.
30790          * @param {Roo.LayoutRegion} this
30791          */
30792         "slideshow" : true,
30793         /**
30794          * @event slidehide
30795          * Fires when this region slides out of view. 
30796          * @param {Roo.LayoutRegion} this
30797          */
30798         "slidehide" : true,
30799         /**
30800          * @event panelactivated
30801          * Fires when a panel is activated. 
30802          * @param {Roo.LayoutRegion} this
30803          * @param {Roo.ContentPanel} panel The activated panel
30804          */
30805         "panelactivated" : true,
30806         /**
30807          * @event resized
30808          * Fires when the user resizes this region. 
30809          * @param {Roo.LayoutRegion} this
30810          * @param {Number} newSize The new size (width for east/west, height for north/south)
30811          */
30812         "resized" : true
30813     };
30814     /** A collection of panels in this region. @type Roo.util.MixedCollection */
30815     this.panels = new Roo.util.MixedCollection();
30816     this.panels.getKey = this.getPanelId.createDelegate(this);
30817     this.box = null;
30818     this.activePanel = null;
30819     // ensure listeners are added...
30820     
30821     if (config.listeners || config.events) {
30822         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
30823             listeners : config.listeners || {},
30824             events : config.events || {}
30825         });
30826     }
30827     
30828     if(skipConfig !== true){
30829         this.applyConfig(config);
30830     }
30831 };
30832
30833 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
30834     getPanelId : function(p){
30835         return p.getId();
30836     },
30837     
30838     applyConfig : function(config){
30839         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30840         this.config = config;
30841         
30842     },
30843     
30844     /**
30845      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
30846      * the width, for horizontal (north, south) the height.
30847      * @param {Number} newSize The new width or height
30848      */
30849     resizeTo : function(newSize){
30850         var el = this.el ? this.el :
30851                  (this.activePanel ? this.activePanel.getEl() : null);
30852         if(el){
30853             switch(this.position){
30854                 case "east":
30855                 case "west":
30856                     el.setWidth(newSize);
30857                     this.fireEvent("resized", this, newSize);
30858                 break;
30859                 case "north":
30860                 case "south":
30861                     el.setHeight(newSize);
30862                     this.fireEvent("resized", this, newSize);
30863                 break;                
30864             }
30865         }
30866     },
30867     
30868     getBox : function(){
30869         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
30870     },
30871     
30872     getMargins : function(){
30873         return this.margins;
30874     },
30875     
30876     updateBox : function(box){
30877         this.box = box;
30878         var el = this.activePanel.getEl();
30879         el.dom.style.left = box.x + "px";
30880         el.dom.style.top = box.y + "px";
30881         this.activePanel.setSize(box.width, box.height);
30882     },
30883     
30884     /**
30885      * Returns the container element for this region.
30886      * @return {Roo.Element}
30887      */
30888     getEl : function(){
30889         return this.activePanel;
30890     },
30891     
30892     /**
30893      * Returns true if this region is currently visible.
30894      * @return {Boolean}
30895      */
30896     isVisible : function(){
30897         return this.activePanel ? true : false;
30898     },
30899     
30900     setActivePanel : function(panel){
30901         panel = this.getPanel(panel);
30902         if(this.activePanel && this.activePanel != panel){
30903             this.activePanel.setActiveState(false);
30904             this.activePanel.getEl().setLeftTop(-10000,-10000);
30905         }
30906         this.activePanel = panel;
30907         panel.setActiveState(true);
30908         if(this.box){
30909             panel.setSize(this.box.width, this.box.height);
30910         }
30911         this.fireEvent("panelactivated", this, panel);
30912         this.fireEvent("invalidated");
30913     },
30914     
30915     /**
30916      * Show the specified panel.
30917      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
30918      * @return {Roo.ContentPanel} The shown panel or null
30919      */
30920     showPanel : function(panel){
30921         if(panel = this.getPanel(panel)){
30922             this.setActivePanel(panel);
30923         }
30924         return panel;
30925     },
30926     
30927     /**
30928      * Get the active panel for this region.
30929      * @return {Roo.ContentPanel} The active panel or null
30930      */
30931     getActivePanel : function(){
30932         return this.activePanel;
30933     },
30934     
30935     /**
30936      * Add the passed ContentPanel(s)
30937      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30938      * @return {Roo.ContentPanel} The panel added (if only one was added)
30939      */
30940     add : function(panel){
30941         if(arguments.length > 1){
30942             for(var i = 0, len = arguments.length; i < len; i++) {
30943                 this.add(arguments[i]);
30944             }
30945             return null;
30946         }
30947         if(this.hasPanel(panel)){
30948             this.showPanel(panel);
30949             return panel;
30950         }
30951         var el = panel.getEl();
30952         if(el.dom.parentNode != this.mgr.el.dom){
30953             this.mgr.el.dom.appendChild(el.dom);
30954         }
30955         if(panel.setRegion){
30956             panel.setRegion(this);
30957         }
30958         this.panels.add(panel);
30959         el.setStyle("position", "absolute");
30960         if(!panel.background){
30961             this.setActivePanel(panel);
30962             if(this.config.initialSize && this.panels.getCount()==1){
30963                 this.resizeTo(this.config.initialSize);
30964             }
30965         }
30966         this.fireEvent("paneladded", this, panel);
30967         return panel;
30968     },
30969     
30970     /**
30971      * Returns true if the panel is in this region.
30972      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30973      * @return {Boolean}
30974      */
30975     hasPanel : function(panel){
30976         if(typeof panel == "object"){ // must be panel obj
30977             panel = panel.getId();
30978         }
30979         return this.getPanel(panel) ? true : false;
30980     },
30981     
30982     /**
30983      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30984      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30985      * @param {Boolean} preservePanel Overrides the config preservePanel option
30986      * @return {Roo.ContentPanel} The panel that was removed
30987      */
30988     remove : function(panel, preservePanel){
30989         panel = this.getPanel(panel);
30990         if(!panel){
30991             return null;
30992         }
30993         var e = {};
30994         this.fireEvent("beforeremove", this, panel, e);
30995         if(e.cancel === true){
30996             return null;
30997         }
30998         var panelId = panel.getId();
30999         this.panels.removeKey(panelId);
31000         return panel;
31001     },
31002     
31003     /**
31004      * Returns the panel specified or null if it's not in this region.
31005      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31006      * @return {Roo.ContentPanel}
31007      */
31008     getPanel : function(id){
31009         if(typeof id == "object"){ // must be panel obj
31010             return id;
31011         }
31012         return this.panels.get(id);
31013     },
31014     
31015     /**
31016      * Returns this regions position (north/south/east/west/center).
31017      * @return {String} 
31018      */
31019     getPosition: function(){
31020         return this.position;    
31021     }
31022 });/*
31023  * Based on:
31024  * Ext JS Library 1.1.1
31025  * Copyright(c) 2006-2007, Ext JS, LLC.
31026  *
31027  * Originally Released Under LGPL - original licence link has changed is not relivant.
31028  *
31029  * Fork - LGPL
31030  * <script type="text/javascript">
31031  */
31032  
31033 /**
31034  * @class Roo.LayoutRegion
31035  * @extends Roo.BasicLayoutRegion
31036  * This class represents a region in a layout manager.
31037  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
31038  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
31039  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
31040  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31041  * @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})
31042  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
31043  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
31044  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
31045  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
31046  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
31047  * @cfg {String}    title           The title for the region (overrides panel titles)
31048  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
31049  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31050  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
31051  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31052  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
31053  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31054  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
31055  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
31056  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
31057  * @cfg {Boolean}   showPin         True to show a pin button
31058  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
31059  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
31060  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
31061  * @cfg {Number}    width           For East/West panels
31062  * @cfg {Number}    height          For North/South panels
31063  * @cfg {Boolean}   split           To show the splitter
31064  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
31065  */
31066 Roo.LayoutRegion = function(mgr, config, pos){
31067     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
31068     var dh = Roo.DomHelper;
31069     /** This region's container element 
31070     * @type Roo.Element */
31071     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
31072     /** This region's title element 
31073     * @type Roo.Element */
31074
31075     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
31076         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
31077         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
31078     ]}, true);
31079     this.titleEl.enableDisplayMode();
31080     /** This region's title text element 
31081     * @type HTMLElement */
31082     this.titleTextEl = this.titleEl.dom.firstChild;
31083     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
31084     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
31085     this.closeBtn.enableDisplayMode();
31086     this.closeBtn.on("click", this.closeClicked, this);
31087     this.closeBtn.hide();
31088
31089     this.createBody(config);
31090     this.visible = true;
31091     this.collapsed = false;
31092
31093     if(config.hideWhenEmpty){
31094         this.hide();
31095         this.on("paneladded", this.validateVisibility, this);
31096         this.on("panelremoved", this.validateVisibility, this);
31097     }
31098     this.applyConfig(config);
31099 };
31100
31101 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
31102
31103     createBody : function(){
31104         /** This region's body element 
31105         * @type Roo.Element */
31106         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
31107     },
31108
31109     applyConfig : function(c){
31110         if(c.collapsible && this.position != "center" && !this.collapsedEl){
31111             var dh = Roo.DomHelper;
31112             if(c.titlebar !== false){
31113                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
31114                 this.collapseBtn.on("click", this.collapse, this);
31115                 this.collapseBtn.enableDisplayMode();
31116
31117                 if(c.showPin === true || this.showPin){
31118                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
31119                     this.stickBtn.enableDisplayMode();
31120                     this.stickBtn.on("click", this.expand, this);
31121                     this.stickBtn.hide();
31122                 }
31123             }
31124             /** This region's collapsed element
31125             * @type Roo.Element */
31126             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
31127                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
31128             ]}, true);
31129             if(c.floatable !== false){
31130                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
31131                this.collapsedEl.on("click", this.collapseClick, this);
31132             }
31133
31134             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
31135                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
31136                    id: "message", unselectable: "on", style:{"float":"left"}});
31137                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
31138              }
31139             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
31140             this.expandBtn.on("click", this.expand, this);
31141         }
31142         if(this.collapseBtn){
31143             this.collapseBtn.setVisible(c.collapsible == true);
31144         }
31145         this.cmargins = c.cmargins || this.cmargins ||
31146                          (this.position == "west" || this.position == "east" ?
31147                              {top: 0, left: 2, right:2, bottom: 0} :
31148                              {top: 2, left: 0, right:0, bottom: 2});
31149         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31150         this.bottomTabs = c.tabPosition != "top";
31151         this.autoScroll = c.autoScroll || false;
31152         if(this.autoScroll){
31153             this.bodyEl.setStyle("overflow", "auto");
31154         }else{
31155             this.bodyEl.setStyle("overflow", "hidden");
31156         }
31157         //if(c.titlebar !== false){
31158             if((!c.titlebar && !c.title) || c.titlebar === false){
31159                 this.titleEl.hide();
31160             }else{
31161                 this.titleEl.show();
31162                 if(c.title){
31163                     this.titleTextEl.innerHTML = c.title;
31164                 }
31165             }
31166         //}
31167         this.duration = c.duration || .30;
31168         this.slideDuration = c.slideDuration || .45;
31169         this.config = c;
31170         if(c.collapsed){
31171             this.collapse(true);
31172         }
31173         if(c.hidden){
31174             this.hide();
31175         }
31176     },
31177     /**
31178      * Returns true if this region is currently visible.
31179      * @return {Boolean}
31180      */
31181     isVisible : function(){
31182         return this.visible;
31183     },
31184
31185     /**
31186      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
31187      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
31188      */
31189     setCollapsedTitle : function(title){
31190         title = title || "&#160;";
31191         if(this.collapsedTitleTextEl){
31192             this.collapsedTitleTextEl.innerHTML = title;
31193         }
31194     },
31195
31196     getBox : function(){
31197         var b;
31198         if(!this.collapsed){
31199             b = this.el.getBox(false, true);
31200         }else{
31201             b = this.collapsedEl.getBox(false, true);
31202         }
31203         return b;
31204     },
31205
31206     getMargins : function(){
31207         return this.collapsed ? this.cmargins : this.margins;
31208     },
31209
31210     highlight : function(){
31211         this.el.addClass("x-layout-panel-dragover");
31212     },
31213
31214     unhighlight : function(){
31215         this.el.removeClass("x-layout-panel-dragover");
31216     },
31217
31218     updateBox : function(box){
31219         this.box = box;
31220         if(!this.collapsed){
31221             this.el.dom.style.left = box.x + "px";
31222             this.el.dom.style.top = box.y + "px";
31223             this.updateBody(box.width, box.height);
31224         }else{
31225             this.collapsedEl.dom.style.left = box.x + "px";
31226             this.collapsedEl.dom.style.top = box.y + "px";
31227             this.collapsedEl.setSize(box.width, box.height);
31228         }
31229         if(this.tabs){
31230             this.tabs.autoSizeTabs();
31231         }
31232     },
31233
31234     updateBody : function(w, h){
31235         if(w !== null){
31236             this.el.setWidth(w);
31237             w -= this.el.getBorderWidth("rl");
31238             if(this.config.adjustments){
31239                 w += this.config.adjustments[0];
31240             }
31241         }
31242         if(h !== null){
31243             this.el.setHeight(h);
31244             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
31245             h -= this.el.getBorderWidth("tb");
31246             if(this.config.adjustments){
31247                 h += this.config.adjustments[1];
31248             }
31249             this.bodyEl.setHeight(h);
31250             if(this.tabs){
31251                 h = this.tabs.syncHeight(h);
31252             }
31253         }
31254         if(this.panelSize){
31255             w = w !== null ? w : this.panelSize.width;
31256             h = h !== null ? h : this.panelSize.height;
31257         }
31258         if(this.activePanel){
31259             var el = this.activePanel.getEl();
31260             w = w !== null ? w : el.getWidth();
31261             h = h !== null ? h : el.getHeight();
31262             this.panelSize = {width: w, height: h};
31263             this.activePanel.setSize(w, h);
31264         }
31265         if(Roo.isIE && this.tabs){
31266             this.tabs.el.repaint();
31267         }
31268     },
31269
31270     /**
31271      * Returns the container element for this region.
31272      * @return {Roo.Element}
31273      */
31274     getEl : function(){
31275         return this.el;
31276     },
31277
31278     /**
31279      * Hides this region.
31280      */
31281     hide : function(){
31282         if(!this.collapsed){
31283             this.el.dom.style.left = "-2000px";
31284             this.el.hide();
31285         }else{
31286             this.collapsedEl.dom.style.left = "-2000px";
31287             this.collapsedEl.hide();
31288         }
31289         this.visible = false;
31290         this.fireEvent("visibilitychange", this, false);
31291     },
31292
31293     /**
31294      * Shows this region if it was previously hidden.
31295      */
31296     show : function(){
31297         if(!this.collapsed){
31298             this.el.show();
31299         }else{
31300             this.collapsedEl.show();
31301         }
31302         this.visible = true;
31303         this.fireEvent("visibilitychange", this, true);
31304     },
31305
31306     closeClicked : function(){
31307         if(this.activePanel){
31308             this.remove(this.activePanel);
31309         }
31310     },
31311
31312     collapseClick : function(e){
31313         if(this.isSlid){
31314            e.stopPropagation();
31315            this.slideIn();
31316         }else{
31317            e.stopPropagation();
31318            this.slideOut();
31319         }
31320     },
31321
31322     /**
31323      * Collapses this region.
31324      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
31325      */
31326     collapse : function(skipAnim){
31327         if(this.collapsed) return;
31328         this.collapsed = true;
31329         if(this.split){
31330             this.split.el.hide();
31331         }
31332         if(this.config.animate && skipAnim !== true){
31333             this.fireEvent("invalidated", this);
31334             this.animateCollapse();
31335         }else{
31336             this.el.setLocation(-20000,-20000);
31337             this.el.hide();
31338             this.collapsedEl.show();
31339             this.fireEvent("collapsed", this);
31340             this.fireEvent("invalidated", this);
31341         }
31342     },
31343
31344     animateCollapse : function(){
31345         // overridden
31346     },
31347
31348     /**
31349      * Expands this region if it was previously collapsed.
31350      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
31351      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
31352      */
31353     expand : function(e, skipAnim){
31354         if(e) e.stopPropagation();
31355         if(!this.collapsed || this.el.hasActiveFx()) return;
31356         if(this.isSlid){
31357             this.afterSlideIn();
31358             skipAnim = true;
31359         }
31360         this.collapsed = false;
31361         if(this.config.animate && skipAnim !== true){
31362             this.animateExpand();
31363         }else{
31364             this.el.show();
31365             if(this.split){
31366                 this.split.el.show();
31367             }
31368             this.collapsedEl.setLocation(-2000,-2000);
31369             this.collapsedEl.hide();
31370             this.fireEvent("invalidated", this);
31371             this.fireEvent("expanded", this);
31372         }
31373     },
31374
31375     animateExpand : function(){
31376         // overridden
31377     },
31378
31379     initTabs : function()
31380     {
31381         this.bodyEl.setStyle("overflow", "hidden");
31382         var ts = new Roo.TabPanel(
31383                 this.bodyEl.dom,
31384                 {
31385                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
31386                     disableTooltips: this.config.disableTabTips,
31387                     toolbar : this.config.toolbar
31388                 }
31389         );
31390         if(this.config.hideTabs){
31391             ts.stripWrap.setDisplayed(false);
31392         }
31393         this.tabs = ts;
31394         ts.resizeTabs = this.config.resizeTabs === true;
31395         ts.minTabWidth = this.config.minTabWidth || 40;
31396         ts.maxTabWidth = this.config.maxTabWidth || 250;
31397         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
31398         ts.monitorResize = false;
31399         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31400         ts.bodyEl.addClass('x-layout-tabs-body');
31401         this.panels.each(this.initPanelAsTab, this);
31402     },
31403
31404     initPanelAsTab : function(panel){
31405         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
31406                     this.config.closeOnTab && panel.isClosable());
31407         if(panel.tabTip !== undefined){
31408             ti.setTooltip(panel.tabTip);
31409         }
31410         ti.on("activate", function(){
31411               this.setActivePanel(panel);
31412         }, this);
31413         if(this.config.closeOnTab){
31414             ti.on("beforeclose", function(t, e){
31415                 e.cancel = true;
31416                 this.remove(panel);
31417             }, this);
31418         }
31419         return ti;
31420     },
31421
31422     updatePanelTitle : function(panel, title){
31423         if(this.activePanel == panel){
31424             this.updateTitle(title);
31425         }
31426         if(this.tabs){
31427             var ti = this.tabs.getTab(panel.getEl().id);
31428             ti.setText(title);
31429             if(panel.tabTip !== undefined){
31430                 ti.setTooltip(panel.tabTip);
31431             }
31432         }
31433     },
31434
31435     updateTitle : function(title){
31436         if(this.titleTextEl && !this.config.title){
31437             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
31438         }
31439     },
31440
31441     setActivePanel : function(panel){
31442         panel = this.getPanel(panel);
31443         if(this.activePanel && this.activePanel != panel){
31444             this.activePanel.setActiveState(false);
31445         }
31446         this.activePanel = panel;
31447         panel.setActiveState(true);
31448         if(this.panelSize){
31449             panel.setSize(this.panelSize.width, this.panelSize.height);
31450         }
31451         if(this.closeBtn){
31452             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
31453         }
31454         this.updateTitle(panel.getTitle());
31455         if(this.tabs){
31456             this.fireEvent("invalidated", this);
31457         }
31458         this.fireEvent("panelactivated", this, panel);
31459     },
31460
31461     /**
31462      * Shows the specified panel.
31463      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
31464      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
31465      */
31466     showPanel : function(panel){
31467         if(panel = this.getPanel(panel)){
31468             if(this.tabs){
31469                 var tab = this.tabs.getTab(panel.getEl().id);
31470                 if(tab.isHidden()){
31471                     this.tabs.unhideTab(tab.id);
31472                 }
31473                 tab.activate();
31474             }else{
31475                 this.setActivePanel(panel);
31476             }
31477         }
31478         return panel;
31479     },
31480
31481     /**
31482      * Get the active panel for this region.
31483      * @return {Roo.ContentPanel} The active panel or null
31484      */
31485     getActivePanel : function(){
31486         return this.activePanel;
31487     },
31488
31489     validateVisibility : function(){
31490         if(this.panels.getCount() < 1){
31491             this.updateTitle("&#160;");
31492             this.closeBtn.hide();
31493             this.hide();
31494         }else{
31495             if(!this.isVisible()){
31496                 this.show();
31497             }
31498         }
31499     },
31500
31501     /**
31502      * Adds the passed ContentPanel(s) to this region.
31503      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31504      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
31505      */
31506     add : function(panel){
31507         if(arguments.length > 1){
31508             for(var i = 0, len = arguments.length; i < len; i++) {
31509                 this.add(arguments[i]);
31510             }
31511             return null;
31512         }
31513         if(this.hasPanel(panel)){
31514             this.showPanel(panel);
31515             return panel;
31516         }
31517         panel.setRegion(this);
31518         this.panels.add(panel);
31519         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
31520             this.bodyEl.dom.appendChild(panel.getEl().dom);
31521             if(panel.background !== true){
31522                 this.setActivePanel(panel);
31523             }
31524             this.fireEvent("paneladded", this, panel);
31525             return panel;
31526         }
31527         if(!this.tabs){
31528             this.initTabs();
31529         }else{
31530             this.initPanelAsTab(panel);
31531         }
31532         if(panel.background !== true){
31533             this.tabs.activate(panel.getEl().id);
31534         }
31535         this.fireEvent("paneladded", this, panel);
31536         return panel;
31537     },
31538
31539     /**
31540      * Hides the tab for the specified panel.
31541      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31542      */
31543     hidePanel : function(panel){
31544         if(this.tabs && (panel = this.getPanel(panel))){
31545             this.tabs.hideTab(panel.getEl().id);
31546         }
31547     },
31548
31549     /**
31550      * Unhides the tab for a previously hidden panel.
31551      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31552      */
31553     unhidePanel : function(panel){
31554         if(this.tabs && (panel = this.getPanel(panel))){
31555             this.tabs.unhideTab(panel.getEl().id);
31556         }
31557     },
31558
31559     clearPanels : function(){
31560         while(this.panels.getCount() > 0){
31561              this.remove(this.panels.first());
31562         }
31563     },
31564
31565     /**
31566      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31567      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31568      * @param {Boolean} preservePanel Overrides the config preservePanel option
31569      * @return {Roo.ContentPanel} The panel that was removed
31570      */
31571     remove : function(panel, preservePanel){
31572         panel = this.getPanel(panel);
31573         if(!panel){
31574             return null;
31575         }
31576         var e = {};
31577         this.fireEvent("beforeremove", this, panel, e);
31578         if(e.cancel === true){
31579             return null;
31580         }
31581         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
31582         var panelId = panel.getId();
31583         this.panels.removeKey(panelId);
31584         if(preservePanel){
31585             document.body.appendChild(panel.getEl().dom);
31586         }
31587         if(this.tabs){
31588             this.tabs.removeTab(panel.getEl().id);
31589         }else if (!preservePanel){
31590             this.bodyEl.dom.removeChild(panel.getEl().dom);
31591         }
31592         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
31593             var p = this.panels.first();
31594             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
31595             tempEl.appendChild(p.getEl().dom);
31596             this.bodyEl.update("");
31597             this.bodyEl.dom.appendChild(p.getEl().dom);
31598             tempEl = null;
31599             this.updateTitle(p.getTitle());
31600             this.tabs = null;
31601             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31602             this.setActivePanel(p);
31603         }
31604         panel.setRegion(null);
31605         if(this.activePanel == panel){
31606             this.activePanel = null;
31607         }
31608         if(this.config.autoDestroy !== false && preservePanel !== true){
31609             try{panel.destroy();}catch(e){}
31610         }
31611         this.fireEvent("panelremoved", this, panel);
31612         return panel;
31613     },
31614
31615     /**
31616      * Returns the TabPanel component used by this region
31617      * @return {Roo.TabPanel}
31618      */
31619     getTabs : function(){
31620         return this.tabs;
31621     },
31622
31623     createTool : function(parentEl, className){
31624         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
31625             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
31626         btn.addClassOnOver("x-layout-tools-button-over");
31627         return btn;
31628     }
31629 });/*
31630  * Based on:
31631  * Ext JS Library 1.1.1
31632  * Copyright(c) 2006-2007, Ext JS, LLC.
31633  *
31634  * Originally Released Under LGPL - original licence link has changed is not relivant.
31635  *
31636  * Fork - LGPL
31637  * <script type="text/javascript">
31638  */
31639  
31640
31641
31642 /**
31643  * @class Roo.SplitLayoutRegion
31644  * @extends Roo.LayoutRegion
31645  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
31646  */
31647 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
31648     this.cursor = cursor;
31649     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
31650 };
31651
31652 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
31653     splitTip : "Drag to resize.",
31654     collapsibleSplitTip : "Drag to resize. Double click to hide.",
31655     useSplitTips : false,
31656
31657     applyConfig : function(config){
31658         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
31659         if(config.split){
31660             if(!this.split){
31661                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
31662                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
31663                 /** The SplitBar for this region 
31664                 * @type Roo.SplitBar */
31665                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
31666                 this.split.on("moved", this.onSplitMove, this);
31667                 this.split.useShim = config.useShim === true;
31668                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
31669                 if(this.useSplitTips){
31670                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
31671                 }
31672                 if(config.collapsible){
31673                     this.split.el.on("dblclick", this.collapse,  this);
31674                 }
31675             }
31676             if(typeof config.minSize != "undefined"){
31677                 this.split.minSize = config.minSize;
31678             }
31679             if(typeof config.maxSize != "undefined"){
31680                 this.split.maxSize = config.maxSize;
31681             }
31682             if(config.hideWhenEmpty || config.hidden || config.collapsed){
31683                 this.hideSplitter();
31684             }
31685         }
31686     },
31687
31688     getHMaxSize : function(){
31689          var cmax = this.config.maxSize || 10000;
31690          var center = this.mgr.getRegion("center");
31691          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
31692     },
31693
31694     getVMaxSize : function(){
31695          var cmax = this.config.maxSize || 10000;
31696          var center = this.mgr.getRegion("center");
31697          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
31698     },
31699
31700     onSplitMove : function(split, newSize){
31701         this.fireEvent("resized", this, newSize);
31702     },
31703     
31704     /** 
31705      * Returns the {@link Roo.SplitBar} for this region.
31706      * @return {Roo.SplitBar}
31707      */
31708     getSplitBar : function(){
31709         return this.split;
31710     },
31711     
31712     hide : function(){
31713         this.hideSplitter();
31714         Roo.SplitLayoutRegion.superclass.hide.call(this);
31715     },
31716
31717     hideSplitter : function(){
31718         if(this.split){
31719             this.split.el.setLocation(-2000,-2000);
31720             this.split.el.hide();
31721         }
31722     },
31723
31724     show : function(){
31725         if(this.split){
31726             this.split.el.show();
31727         }
31728         Roo.SplitLayoutRegion.superclass.show.call(this);
31729     },
31730     
31731     beforeSlide: function(){
31732         if(Roo.isGecko){// firefox overflow auto bug workaround
31733             this.bodyEl.clip();
31734             if(this.tabs) this.tabs.bodyEl.clip();
31735             if(this.activePanel){
31736                 this.activePanel.getEl().clip();
31737                 
31738                 if(this.activePanel.beforeSlide){
31739                     this.activePanel.beforeSlide();
31740                 }
31741             }
31742         }
31743     },
31744     
31745     afterSlide : function(){
31746         if(Roo.isGecko){// firefox overflow auto bug workaround
31747             this.bodyEl.unclip();
31748             if(this.tabs) this.tabs.bodyEl.unclip();
31749             if(this.activePanel){
31750                 this.activePanel.getEl().unclip();
31751                 if(this.activePanel.afterSlide){
31752                     this.activePanel.afterSlide();
31753                 }
31754             }
31755         }
31756     },
31757
31758     initAutoHide : function(){
31759         if(this.autoHide !== false){
31760             if(!this.autoHideHd){
31761                 var st = new Roo.util.DelayedTask(this.slideIn, this);
31762                 this.autoHideHd = {
31763                     "mouseout": function(e){
31764                         if(!e.within(this.el, true)){
31765                             st.delay(500);
31766                         }
31767                     },
31768                     "mouseover" : function(e){
31769                         st.cancel();
31770                     },
31771                     scope : this
31772                 };
31773             }
31774             this.el.on(this.autoHideHd);
31775         }
31776     },
31777
31778     clearAutoHide : function(){
31779         if(this.autoHide !== false){
31780             this.el.un("mouseout", this.autoHideHd.mouseout);
31781             this.el.un("mouseover", this.autoHideHd.mouseover);
31782         }
31783     },
31784
31785     clearMonitor : function(){
31786         Roo.get(document).un("click", this.slideInIf, this);
31787     },
31788
31789     // these names are backwards but not changed for compat
31790     slideOut : function(){
31791         if(this.isSlid || this.el.hasActiveFx()){
31792             return;
31793         }
31794         this.isSlid = true;
31795         if(this.collapseBtn){
31796             this.collapseBtn.hide();
31797         }
31798         this.closeBtnState = this.closeBtn.getStyle('display');
31799         this.closeBtn.hide();
31800         if(this.stickBtn){
31801             this.stickBtn.show();
31802         }
31803         this.el.show();
31804         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
31805         this.beforeSlide();
31806         this.el.setStyle("z-index", 10001);
31807         this.el.slideIn(this.getSlideAnchor(), {
31808             callback: function(){
31809                 this.afterSlide();
31810                 this.initAutoHide();
31811                 Roo.get(document).on("click", this.slideInIf, this);
31812                 this.fireEvent("slideshow", this);
31813             },
31814             scope: this,
31815             block: true
31816         });
31817     },
31818
31819     afterSlideIn : function(){
31820         this.clearAutoHide();
31821         this.isSlid = false;
31822         this.clearMonitor();
31823         this.el.setStyle("z-index", "");
31824         if(this.collapseBtn){
31825             this.collapseBtn.show();
31826         }
31827         this.closeBtn.setStyle('display', this.closeBtnState);
31828         if(this.stickBtn){
31829             this.stickBtn.hide();
31830         }
31831         this.fireEvent("slidehide", this);
31832     },
31833
31834     slideIn : function(cb){
31835         if(!this.isSlid || this.el.hasActiveFx()){
31836             Roo.callback(cb);
31837             return;
31838         }
31839         this.isSlid = false;
31840         this.beforeSlide();
31841         this.el.slideOut(this.getSlideAnchor(), {
31842             callback: function(){
31843                 this.el.setLeftTop(-10000, -10000);
31844                 this.afterSlide();
31845                 this.afterSlideIn();
31846                 Roo.callback(cb);
31847             },
31848             scope: this,
31849             block: true
31850         });
31851     },
31852     
31853     slideInIf : function(e){
31854         if(!e.within(this.el)){
31855             this.slideIn();
31856         }
31857     },
31858
31859     animateCollapse : function(){
31860         this.beforeSlide();
31861         this.el.setStyle("z-index", 20000);
31862         var anchor = this.getSlideAnchor();
31863         this.el.slideOut(anchor, {
31864             callback : function(){
31865                 this.el.setStyle("z-index", "");
31866                 this.collapsedEl.slideIn(anchor, {duration:.3});
31867                 this.afterSlide();
31868                 this.el.setLocation(-10000,-10000);
31869                 this.el.hide();
31870                 this.fireEvent("collapsed", this);
31871             },
31872             scope: this,
31873             block: true
31874         });
31875     },
31876
31877     animateExpand : function(){
31878         this.beforeSlide();
31879         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
31880         this.el.setStyle("z-index", 20000);
31881         this.collapsedEl.hide({
31882             duration:.1
31883         });
31884         this.el.slideIn(this.getSlideAnchor(), {
31885             callback : function(){
31886                 this.el.setStyle("z-index", "");
31887                 this.afterSlide();
31888                 if(this.split){
31889                     this.split.el.show();
31890                 }
31891                 this.fireEvent("invalidated", this);
31892                 this.fireEvent("expanded", this);
31893             },
31894             scope: this,
31895             block: true
31896         });
31897     },
31898
31899     anchors : {
31900         "west" : "left",
31901         "east" : "right",
31902         "north" : "top",
31903         "south" : "bottom"
31904     },
31905
31906     sanchors : {
31907         "west" : "l",
31908         "east" : "r",
31909         "north" : "t",
31910         "south" : "b"
31911     },
31912
31913     canchors : {
31914         "west" : "tl-tr",
31915         "east" : "tr-tl",
31916         "north" : "tl-bl",
31917         "south" : "bl-tl"
31918     },
31919
31920     getAnchor : function(){
31921         return this.anchors[this.position];
31922     },
31923
31924     getCollapseAnchor : function(){
31925         return this.canchors[this.position];
31926     },
31927
31928     getSlideAnchor : function(){
31929         return this.sanchors[this.position];
31930     },
31931
31932     getAlignAdj : function(){
31933         var cm = this.cmargins;
31934         switch(this.position){
31935             case "west":
31936                 return [0, 0];
31937             break;
31938             case "east":
31939                 return [0, 0];
31940             break;
31941             case "north":
31942                 return [0, 0];
31943             break;
31944             case "south":
31945                 return [0, 0];
31946             break;
31947         }
31948     },
31949
31950     getExpandAdj : function(){
31951         var c = this.collapsedEl, cm = this.cmargins;
31952         switch(this.position){
31953             case "west":
31954                 return [-(cm.right+c.getWidth()+cm.left), 0];
31955             break;
31956             case "east":
31957                 return [cm.right+c.getWidth()+cm.left, 0];
31958             break;
31959             case "north":
31960                 return [0, -(cm.top+cm.bottom+c.getHeight())];
31961             break;
31962             case "south":
31963                 return [0, cm.top+cm.bottom+c.getHeight()];
31964             break;
31965         }
31966     }
31967 });/*
31968  * Based on:
31969  * Ext JS Library 1.1.1
31970  * Copyright(c) 2006-2007, Ext JS, LLC.
31971  *
31972  * Originally Released Under LGPL - original licence link has changed is not relivant.
31973  *
31974  * Fork - LGPL
31975  * <script type="text/javascript">
31976  */
31977 /*
31978  * These classes are private internal classes
31979  */
31980 Roo.CenterLayoutRegion = function(mgr, config){
31981     Roo.LayoutRegion.call(this, mgr, config, "center");
31982     this.visible = true;
31983     this.minWidth = config.minWidth || 20;
31984     this.minHeight = config.minHeight || 20;
31985 };
31986
31987 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31988     hide : function(){
31989         // center panel can't be hidden
31990     },
31991     
31992     show : function(){
31993         // center panel can't be hidden
31994     },
31995     
31996     getMinWidth: function(){
31997         return this.minWidth;
31998     },
31999     
32000     getMinHeight: function(){
32001         return this.minHeight;
32002     }
32003 });
32004
32005
32006 Roo.NorthLayoutRegion = function(mgr, config){
32007     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
32008     if(this.split){
32009         this.split.placement = Roo.SplitBar.TOP;
32010         this.split.orientation = Roo.SplitBar.VERTICAL;
32011         this.split.el.addClass("x-layout-split-v");
32012     }
32013     var size = config.initialSize || config.height;
32014     if(typeof size != "undefined"){
32015         this.el.setHeight(size);
32016     }
32017 };
32018 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
32019     orientation: Roo.SplitBar.VERTICAL,
32020     getBox : function(){
32021         if(this.collapsed){
32022             return this.collapsedEl.getBox();
32023         }
32024         var box = this.el.getBox();
32025         if(this.split){
32026             box.height += this.split.el.getHeight();
32027         }
32028         return box;
32029     },
32030     
32031     updateBox : function(box){
32032         if(this.split && !this.collapsed){
32033             box.height -= this.split.el.getHeight();
32034             this.split.el.setLeft(box.x);
32035             this.split.el.setTop(box.y+box.height);
32036             this.split.el.setWidth(box.width);
32037         }
32038         if(this.collapsed){
32039             this.updateBody(box.width, null);
32040         }
32041         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32042     }
32043 });
32044
32045 Roo.SouthLayoutRegion = function(mgr, config){
32046     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
32047     if(this.split){
32048         this.split.placement = Roo.SplitBar.BOTTOM;
32049         this.split.orientation = Roo.SplitBar.VERTICAL;
32050         this.split.el.addClass("x-layout-split-v");
32051     }
32052     var size = config.initialSize || config.height;
32053     if(typeof size != "undefined"){
32054         this.el.setHeight(size);
32055     }
32056 };
32057 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
32058     orientation: Roo.SplitBar.VERTICAL,
32059     getBox : function(){
32060         if(this.collapsed){
32061             return this.collapsedEl.getBox();
32062         }
32063         var box = this.el.getBox();
32064         if(this.split){
32065             var sh = this.split.el.getHeight();
32066             box.height += sh;
32067             box.y -= sh;
32068         }
32069         return box;
32070     },
32071     
32072     updateBox : function(box){
32073         if(this.split && !this.collapsed){
32074             var sh = this.split.el.getHeight();
32075             box.height -= sh;
32076             box.y += sh;
32077             this.split.el.setLeft(box.x);
32078             this.split.el.setTop(box.y-sh);
32079             this.split.el.setWidth(box.width);
32080         }
32081         if(this.collapsed){
32082             this.updateBody(box.width, null);
32083         }
32084         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32085     }
32086 });
32087
32088 Roo.EastLayoutRegion = function(mgr, config){
32089     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
32090     if(this.split){
32091         this.split.placement = Roo.SplitBar.RIGHT;
32092         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32093         this.split.el.addClass("x-layout-split-h");
32094     }
32095     var size = config.initialSize || config.width;
32096     if(typeof size != "undefined"){
32097         this.el.setWidth(size);
32098     }
32099 };
32100 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
32101     orientation: Roo.SplitBar.HORIZONTAL,
32102     getBox : function(){
32103         if(this.collapsed){
32104             return this.collapsedEl.getBox();
32105         }
32106         var box = this.el.getBox();
32107         if(this.split){
32108             var sw = this.split.el.getWidth();
32109             box.width += sw;
32110             box.x -= sw;
32111         }
32112         return box;
32113     },
32114
32115     updateBox : function(box){
32116         if(this.split && !this.collapsed){
32117             var sw = this.split.el.getWidth();
32118             box.width -= sw;
32119             this.split.el.setLeft(box.x);
32120             this.split.el.setTop(box.y);
32121             this.split.el.setHeight(box.height);
32122             box.x += sw;
32123         }
32124         if(this.collapsed){
32125             this.updateBody(null, box.height);
32126         }
32127         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32128     }
32129 });
32130
32131 Roo.WestLayoutRegion = function(mgr, config){
32132     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
32133     if(this.split){
32134         this.split.placement = Roo.SplitBar.LEFT;
32135         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32136         this.split.el.addClass("x-layout-split-h");
32137     }
32138     var size = config.initialSize || config.width;
32139     if(typeof size != "undefined"){
32140         this.el.setWidth(size);
32141     }
32142 };
32143 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
32144     orientation: Roo.SplitBar.HORIZONTAL,
32145     getBox : function(){
32146         if(this.collapsed){
32147             return this.collapsedEl.getBox();
32148         }
32149         var box = this.el.getBox();
32150         if(this.split){
32151             box.width += this.split.el.getWidth();
32152         }
32153         return box;
32154     },
32155     
32156     updateBox : function(box){
32157         if(this.split && !this.collapsed){
32158             var sw = this.split.el.getWidth();
32159             box.width -= sw;
32160             this.split.el.setLeft(box.x+box.width);
32161             this.split.el.setTop(box.y);
32162             this.split.el.setHeight(box.height);
32163         }
32164         if(this.collapsed){
32165             this.updateBody(null, box.height);
32166         }
32167         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32168     }
32169 });
32170 /*
32171  * Based on:
32172  * Ext JS Library 1.1.1
32173  * Copyright(c) 2006-2007, Ext JS, LLC.
32174  *
32175  * Originally Released Under LGPL - original licence link has changed is not relivant.
32176  *
32177  * Fork - LGPL
32178  * <script type="text/javascript">
32179  */
32180  
32181  
32182 /*
32183  * Private internal class for reading and applying state
32184  */
32185 Roo.LayoutStateManager = function(layout){
32186      // default empty state
32187      this.state = {
32188         north: {},
32189         south: {},
32190         east: {},
32191         west: {}       
32192     };
32193 };
32194
32195 Roo.LayoutStateManager.prototype = {
32196     init : function(layout, provider){
32197         this.provider = provider;
32198         var state = provider.get(layout.id+"-layout-state");
32199         if(state){
32200             var wasUpdating = layout.isUpdating();
32201             if(!wasUpdating){
32202                 layout.beginUpdate();
32203             }
32204             for(var key in state){
32205                 if(typeof state[key] != "function"){
32206                     var rstate = state[key];
32207                     var r = layout.getRegion(key);
32208                     if(r && rstate){
32209                         if(rstate.size){
32210                             r.resizeTo(rstate.size);
32211                         }
32212                         if(rstate.collapsed == true){
32213                             r.collapse(true);
32214                         }else{
32215                             r.expand(null, true);
32216                         }
32217                     }
32218                 }
32219             }
32220             if(!wasUpdating){
32221                 layout.endUpdate();
32222             }
32223             this.state = state; 
32224         }
32225         this.layout = layout;
32226         layout.on("regionresized", this.onRegionResized, this);
32227         layout.on("regioncollapsed", this.onRegionCollapsed, this);
32228         layout.on("regionexpanded", this.onRegionExpanded, this);
32229     },
32230     
32231     storeState : function(){
32232         this.provider.set(this.layout.id+"-layout-state", this.state);
32233     },
32234     
32235     onRegionResized : function(region, newSize){
32236         this.state[region.getPosition()].size = newSize;
32237         this.storeState();
32238     },
32239     
32240     onRegionCollapsed : function(region){
32241         this.state[region.getPosition()].collapsed = true;
32242         this.storeState();
32243     },
32244     
32245     onRegionExpanded : function(region){
32246         this.state[region.getPosition()].collapsed = false;
32247         this.storeState();
32248     }
32249 };/*
32250  * Based on:
32251  * Ext JS Library 1.1.1
32252  * Copyright(c) 2006-2007, Ext JS, LLC.
32253  *
32254  * Originally Released Under LGPL - original licence link has changed is not relivant.
32255  *
32256  * Fork - LGPL
32257  * <script type="text/javascript">
32258  */
32259 /**
32260  * @class Roo.ContentPanel
32261  * @extends Roo.util.Observable
32262  * A basic ContentPanel element.
32263  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
32264  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
32265  * @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
32266  * @cfg {Boolean}   closable      True if the panel can be closed/removed
32267  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
32268  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
32269  * @cfg {Toolbar}   toolbar       A toolbar for this panel
32270  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
32271  * @cfg {String} title          The title for this panel
32272  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
32273  * @cfg {String} url            Calls {@link #setUrl} with this value
32274  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
32275  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
32276  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
32277  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
32278
32279  * @constructor
32280  * Create a new ContentPanel.
32281  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
32282  * @param {String/Object} config A string to set only the title or a config object
32283  * @param {String} content (optional) Set the HTML content for this panel
32284  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
32285  */
32286 Roo.ContentPanel = function(el, config, content){
32287     
32288      
32289     /*
32290     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
32291         config = el;
32292         el = Roo.id();
32293     }
32294     if (config && config.parentLayout) { 
32295         el = config.parentLayout.el.createChild(); 
32296     }
32297     */
32298     if(el.autoCreate){ // xtype is available if this is called from factory
32299         config = el;
32300         el = Roo.id();
32301     }
32302     this.el = Roo.get(el);
32303     if(!this.el && config && config.autoCreate){
32304         if(typeof config.autoCreate == "object"){
32305             if(!config.autoCreate.id){
32306                 config.autoCreate.id = config.id||el;
32307             }
32308             this.el = Roo.DomHelper.append(document.body,
32309                         config.autoCreate, true);
32310         }else{
32311             this.el = Roo.DomHelper.append(document.body,
32312                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
32313         }
32314     }
32315     this.closable = false;
32316     this.loaded = false;
32317     this.active = false;
32318     if(typeof config == "string"){
32319         this.title = config;
32320     }else{
32321         Roo.apply(this, config);
32322     }
32323     
32324     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
32325         this.wrapEl = this.el.wrap();
32326         this.toolbar.container = this.el.insertSibling(false, 'before');
32327         this.toolbar = new Roo.Toolbar(this.toolbar);
32328     }
32329     
32330     
32331     
32332     if(this.resizeEl){
32333         this.resizeEl = Roo.get(this.resizeEl, true);
32334     }else{
32335         this.resizeEl = this.el;
32336     }
32337     this.addEvents({
32338         /**
32339          * @event activate
32340          * Fires when this panel is activated. 
32341          * @param {Roo.ContentPanel} this
32342          */
32343         "activate" : true,
32344         /**
32345          * @event deactivate
32346          * Fires when this panel is activated. 
32347          * @param {Roo.ContentPanel} this
32348          */
32349         "deactivate" : true,
32350
32351         /**
32352          * @event resize
32353          * Fires when this panel is resized if fitToFrame is true.
32354          * @param {Roo.ContentPanel} this
32355          * @param {Number} width The width after any component adjustments
32356          * @param {Number} height The height after any component adjustments
32357          */
32358         "resize" : true,
32359         
32360          /**
32361          * @event render
32362          * Fires when this tab is created
32363          * @param {Roo.ContentPanel} this
32364          */
32365         "render" : true
32366         
32367         
32368         
32369     });
32370     if(this.autoScroll){
32371         this.resizeEl.setStyle("overflow", "auto");
32372     } else {
32373         // fix randome scrolling
32374         this.el.on('scroll', function() {
32375             Roo.log('fix random scolling');
32376             this.scrollTo('top',0); 
32377         });
32378     }
32379     content = content || this.content;
32380     if(content){
32381         this.setContent(content);
32382     }
32383     if(config && config.url){
32384         this.setUrl(this.url, this.params, this.loadOnce);
32385     }
32386     
32387     
32388     
32389     Roo.ContentPanel.superclass.constructor.call(this);
32390     
32391     this.fireEvent('render', this);
32392 };
32393
32394 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
32395     tabTip:'',
32396     setRegion : function(region){
32397         this.region = region;
32398         if(region){
32399            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
32400         }else{
32401            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
32402         } 
32403     },
32404     
32405     /**
32406      * Returns the toolbar for this Panel if one was configured. 
32407      * @return {Roo.Toolbar} 
32408      */
32409     getToolbar : function(){
32410         return this.toolbar;
32411     },
32412     
32413     setActiveState : function(active){
32414         this.active = active;
32415         if(!active){
32416             this.fireEvent("deactivate", this);
32417         }else{
32418             this.fireEvent("activate", this);
32419         }
32420     },
32421     /**
32422      * Updates this panel's element
32423      * @param {String} content The new content
32424      * @param {Boolean} loadScripts (optional) true to look for and process scripts
32425     */
32426     setContent : function(content, loadScripts){
32427         this.el.update(content, loadScripts);
32428     },
32429
32430     ignoreResize : function(w, h){
32431         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
32432             return true;
32433         }else{
32434             this.lastSize = {width: w, height: h};
32435             return false;
32436         }
32437     },
32438     /**
32439      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
32440      * @return {Roo.UpdateManager} The UpdateManager
32441      */
32442     getUpdateManager : function(){
32443         return this.el.getUpdateManager();
32444     },
32445      /**
32446      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
32447      * @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:
32448 <pre><code>
32449 panel.load({
32450     url: "your-url.php",
32451     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
32452     callback: yourFunction,
32453     scope: yourObject, //(optional scope)
32454     discardUrl: false,
32455     nocache: false,
32456     text: "Loading...",
32457     timeout: 30,
32458     scripts: false
32459 });
32460 </code></pre>
32461      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
32462      * 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.
32463      * @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}
32464      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
32465      * @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.
32466      * @return {Roo.ContentPanel} this
32467      */
32468     load : function(){
32469         var um = this.el.getUpdateManager();
32470         um.update.apply(um, arguments);
32471         return this;
32472     },
32473
32474
32475     /**
32476      * 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.
32477      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
32478      * @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)
32479      * @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)
32480      * @return {Roo.UpdateManager} The UpdateManager
32481      */
32482     setUrl : function(url, params, loadOnce){
32483         if(this.refreshDelegate){
32484             this.removeListener("activate", this.refreshDelegate);
32485         }
32486         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
32487         this.on("activate", this.refreshDelegate);
32488         return this.el.getUpdateManager();
32489     },
32490     
32491     _handleRefresh : function(url, params, loadOnce){
32492         if(!loadOnce || !this.loaded){
32493             var updater = this.el.getUpdateManager();
32494             updater.update(url, params, this._setLoaded.createDelegate(this));
32495         }
32496     },
32497     
32498     _setLoaded : function(){
32499         this.loaded = true;
32500     }, 
32501     
32502     /**
32503      * Returns this panel's id
32504      * @return {String} 
32505      */
32506     getId : function(){
32507         return this.el.id;
32508     },
32509     
32510     /** 
32511      * Returns this panel's element - used by regiosn to add.
32512      * @return {Roo.Element} 
32513      */
32514     getEl : function(){
32515         return this.wrapEl || this.el;
32516     },
32517     
32518     adjustForComponents : function(width, height){
32519         if(this.resizeEl != this.el){
32520             width -= this.el.getFrameWidth('lr');
32521             height -= this.el.getFrameWidth('tb');
32522         }
32523         if(this.toolbar){
32524             var te = this.toolbar.getEl();
32525             height -= te.getHeight();
32526             te.setWidth(width);
32527         }
32528         if(this.adjustments){
32529             width += this.adjustments[0];
32530             height += this.adjustments[1];
32531         }
32532         return {"width": width, "height": height};
32533     },
32534     
32535     setSize : function(width, height){
32536         if(this.fitToFrame && !this.ignoreResize(width, height)){
32537             if(this.fitContainer && this.resizeEl != this.el){
32538                 this.el.setSize(width, height);
32539             }
32540             var size = this.adjustForComponents(width, height);
32541             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
32542             this.fireEvent('resize', this, size.width, size.height);
32543         }
32544     },
32545     
32546     /**
32547      * Returns this panel's title
32548      * @return {String} 
32549      */
32550     getTitle : function(){
32551         return this.title;
32552     },
32553     
32554     /**
32555      * Set this panel's title
32556      * @param {String} title
32557      */
32558     setTitle : function(title){
32559         this.title = title;
32560         if(this.region){
32561             this.region.updatePanelTitle(this, title);
32562         }
32563     },
32564     
32565     /**
32566      * Returns true is this panel was configured to be closable
32567      * @return {Boolean} 
32568      */
32569     isClosable : function(){
32570         return this.closable;
32571     },
32572     
32573     beforeSlide : function(){
32574         this.el.clip();
32575         this.resizeEl.clip();
32576     },
32577     
32578     afterSlide : function(){
32579         this.el.unclip();
32580         this.resizeEl.unclip();
32581     },
32582     
32583     /**
32584      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
32585      *   Will fail silently if the {@link #setUrl} method has not been called.
32586      *   This does not activate the panel, just updates its content.
32587      */
32588     refresh : function(){
32589         if(this.refreshDelegate){
32590            this.loaded = false;
32591            this.refreshDelegate();
32592         }
32593     },
32594     
32595     /**
32596      * Destroys this panel
32597      */
32598     destroy : function(){
32599         this.el.removeAllListeners();
32600         var tempEl = document.createElement("span");
32601         tempEl.appendChild(this.el.dom);
32602         tempEl.innerHTML = "";
32603         this.el.remove();
32604         this.el = null;
32605     },
32606     
32607     /**
32608      * form - if the content panel contains a form - this is a reference to it.
32609      * @type {Roo.form.Form}
32610      */
32611     form : false,
32612     /**
32613      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
32614      *    This contains a reference to it.
32615      * @type {Roo.View}
32616      */
32617     view : false,
32618     
32619       /**
32620      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
32621      * <pre><code>
32622
32623 layout.addxtype({
32624        xtype : 'Form',
32625        items: [ .... ]
32626    }
32627 );
32628
32629 </code></pre>
32630      * @param {Object} cfg Xtype definition of item to add.
32631      */
32632     
32633     addxtype : function(cfg) {
32634         // add form..
32635         if (cfg.xtype.match(/^Form$/)) {
32636             var el = this.el.createChild();
32637
32638             this.form = new  Roo.form.Form(cfg);
32639             
32640             
32641             if ( this.form.allItems.length) this.form.render(el.dom);
32642             return this.form;
32643         }
32644         // should only have one of theses..
32645         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
32646             // views..
32647             cfg.el = this.el.appendChild(document.createElement("div"));
32648             // factory?
32649             
32650             var ret = new Roo.factory(cfg);
32651             ret.render && ret.render(false, ''); // render blank..
32652             this.view = ret;
32653             return ret;
32654         }
32655         return false;
32656     }
32657 });
32658
32659 /**
32660  * @class Roo.GridPanel
32661  * @extends Roo.ContentPanel
32662  * @constructor
32663  * Create a new GridPanel.
32664  * @param {Roo.grid.Grid} grid The grid for this panel
32665  * @param {String/Object} config A string to set only the panel's title, or a config object
32666  */
32667 Roo.GridPanel = function(grid, config){
32668     
32669   
32670     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
32671         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
32672         
32673     this.wrapper.dom.appendChild(grid.getGridEl().dom);
32674     
32675     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
32676     
32677     if(this.toolbar){
32678         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
32679     }
32680     // xtype created footer. - not sure if will work as we normally have to render first..
32681     if (this.footer && !this.footer.el && this.footer.xtype) {
32682         
32683         this.footer.container = this.grid.getView().getFooterPanel(true);
32684         this.footer.dataSource = this.grid.dataSource;
32685         this.footer = Roo.factory(this.footer, Roo);
32686         
32687     }
32688     
32689     grid.monitorWindowResize = false; // turn off autosizing
32690     grid.autoHeight = false;
32691     grid.autoWidth = false;
32692     this.grid = grid;
32693     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
32694 };
32695
32696 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
32697     getId : function(){
32698         return this.grid.id;
32699     },
32700     
32701     /**
32702      * Returns the grid for this panel
32703      * @return {Roo.grid.Grid} 
32704      */
32705     getGrid : function(){
32706         return this.grid;    
32707     },
32708     
32709     setSize : function(width, height){
32710         if(!this.ignoreResize(width, height)){
32711             var grid = this.grid;
32712             var size = this.adjustForComponents(width, height);
32713             grid.getGridEl().setSize(size.width, size.height);
32714             grid.autoSize();
32715         }
32716     },
32717     
32718     beforeSlide : function(){
32719         this.grid.getView().scroller.clip();
32720     },
32721     
32722     afterSlide : function(){
32723         this.grid.getView().scroller.unclip();
32724     },
32725     
32726     destroy : function(){
32727         this.grid.destroy();
32728         delete this.grid;
32729         Roo.GridPanel.superclass.destroy.call(this); 
32730     }
32731 });
32732
32733
32734 /**
32735  * @class Roo.NestedLayoutPanel
32736  * @extends Roo.ContentPanel
32737  * @constructor
32738  * Create a new NestedLayoutPanel.
32739  * 
32740  * 
32741  * @param {Roo.BorderLayout} layout The layout for this panel
32742  * @param {String/Object} config A string to set only the title or a config object
32743  */
32744 Roo.NestedLayoutPanel = function(layout, config)
32745 {
32746     // construct with only one argument..
32747     /* FIXME - implement nicer consturctors
32748     if (layout.layout) {
32749         config = layout;
32750         layout = config.layout;
32751         delete config.layout;
32752     }
32753     if (layout.xtype && !layout.getEl) {
32754         // then layout needs constructing..
32755         layout = Roo.factory(layout, Roo);
32756     }
32757     */
32758     
32759     
32760     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
32761     
32762     layout.monitorWindowResize = false; // turn off autosizing
32763     this.layout = layout;
32764     this.layout.getEl().addClass("x-layout-nested-layout");
32765     
32766     
32767     
32768     
32769 };
32770
32771 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
32772
32773     setSize : function(width, height){
32774         if(!this.ignoreResize(width, height)){
32775             var size = this.adjustForComponents(width, height);
32776             var el = this.layout.getEl();
32777             el.setSize(size.width, size.height);
32778             var touch = el.dom.offsetWidth;
32779             this.layout.layout();
32780             // ie requires a double layout on the first pass
32781             if(Roo.isIE && !this.initialized){
32782                 this.initialized = true;
32783                 this.layout.layout();
32784             }
32785         }
32786     },
32787     
32788     // activate all subpanels if not currently active..
32789     
32790     setActiveState : function(active){
32791         this.active = active;
32792         if(!active){
32793             this.fireEvent("deactivate", this);
32794             return;
32795         }
32796         
32797         this.fireEvent("activate", this);
32798         // not sure if this should happen before or after..
32799         if (!this.layout) {
32800             return; // should not happen..
32801         }
32802         var reg = false;
32803         for (var r in this.layout.regions) {
32804             reg = this.layout.getRegion(r);
32805             if (reg.getActivePanel()) {
32806                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
32807                 reg.setActivePanel(reg.getActivePanel());
32808                 continue;
32809             }
32810             if (!reg.panels.length) {
32811                 continue;
32812             }
32813             reg.showPanel(reg.getPanel(0));
32814         }
32815         
32816         
32817         
32818         
32819     },
32820     
32821     /**
32822      * Returns the nested BorderLayout for this panel
32823      * @return {Roo.BorderLayout} 
32824      */
32825     getLayout : function(){
32826         return this.layout;
32827     },
32828     
32829      /**
32830      * Adds a xtype elements to the layout of the nested panel
32831      * <pre><code>
32832
32833 panel.addxtype({
32834        xtype : 'ContentPanel',
32835        region: 'west',
32836        items: [ .... ]
32837    }
32838 );
32839
32840 panel.addxtype({
32841         xtype : 'NestedLayoutPanel',
32842         region: 'west',
32843         layout: {
32844            center: { },
32845            west: { }   
32846         },
32847         items : [ ... list of content panels or nested layout panels.. ]
32848    }
32849 );
32850 </code></pre>
32851      * @param {Object} cfg Xtype definition of item to add.
32852      */
32853     addxtype : function(cfg) {
32854         return this.layout.addxtype(cfg);
32855     
32856     }
32857 });
32858
32859 Roo.ScrollPanel = function(el, config, content){
32860     config = config || {};
32861     config.fitToFrame = true;
32862     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
32863     
32864     this.el.dom.style.overflow = "hidden";
32865     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
32866     this.el.removeClass("x-layout-inactive-content");
32867     this.el.on("mousewheel", this.onWheel, this);
32868
32869     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
32870     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
32871     up.unselectable(); down.unselectable();
32872     up.on("click", this.scrollUp, this);
32873     down.on("click", this.scrollDown, this);
32874     up.addClassOnOver("x-scroller-btn-over");
32875     down.addClassOnOver("x-scroller-btn-over");
32876     up.addClassOnClick("x-scroller-btn-click");
32877     down.addClassOnClick("x-scroller-btn-click");
32878     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
32879
32880     this.resizeEl = this.el;
32881     this.el = wrap; this.up = up; this.down = down;
32882 };
32883
32884 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
32885     increment : 100,
32886     wheelIncrement : 5,
32887     scrollUp : function(){
32888         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
32889     },
32890
32891     scrollDown : function(){
32892         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
32893     },
32894
32895     afterScroll : function(){
32896         var el = this.resizeEl;
32897         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
32898         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32899         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32900     },
32901
32902     setSize : function(){
32903         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
32904         this.afterScroll();
32905     },
32906
32907     onWheel : function(e){
32908         var d = e.getWheelDelta();
32909         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
32910         this.afterScroll();
32911         e.stopEvent();
32912     },
32913
32914     setContent : function(content, loadScripts){
32915         this.resizeEl.update(content, loadScripts);
32916     }
32917
32918 });
32919
32920
32921
32922
32923
32924
32925
32926
32927
32928 /**
32929  * @class Roo.TreePanel
32930  * @extends Roo.ContentPanel
32931  * @constructor
32932  * Create a new TreePanel. - defaults to fit/scoll contents.
32933  * @param {String/Object} config A string to set only the panel's title, or a config object
32934  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32935  */
32936 Roo.TreePanel = function(config){
32937     var el = config.el;
32938     var tree = config.tree;
32939     delete config.tree; 
32940     delete config.el; // hopefull!
32941     
32942     // wrapper for IE7 strict & safari scroll issue
32943     
32944     var treeEl = el.createChild();
32945     config.resizeEl = treeEl;
32946     
32947     
32948     
32949     Roo.TreePanel.superclass.constructor.call(this, el, config);
32950  
32951  
32952     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32953     //console.log(tree);
32954     this.on('activate', function()
32955     {
32956         if (this.tree.rendered) {
32957             return;
32958         }
32959         //console.log('render tree');
32960         this.tree.render();
32961     });
32962     
32963     this.on('resize',  function (cp, w, h) {
32964             this.tree.innerCt.setWidth(w);
32965             this.tree.innerCt.setHeight(h);
32966             this.tree.innerCt.setStyle('overflow-y', 'auto');
32967     });
32968
32969         
32970     
32971 };
32972
32973 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32974     fitToFrame : true,
32975     autoScroll : true
32976 });
32977
32978
32979
32980
32981
32982
32983
32984
32985
32986
32987
32988 /*
32989  * Based on:
32990  * Ext JS Library 1.1.1
32991  * Copyright(c) 2006-2007, Ext JS, LLC.
32992  *
32993  * Originally Released Under LGPL - original licence link has changed is not relivant.
32994  *
32995  * Fork - LGPL
32996  * <script type="text/javascript">
32997  */
32998  
32999
33000 /**
33001  * @class Roo.ReaderLayout
33002  * @extends Roo.BorderLayout
33003  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
33004  * center region containing two nested regions (a top one for a list view and one for item preview below),
33005  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
33006  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
33007  * expedites the setup of the overall layout and regions for this common application style.
33008  * Example:
33009  <pre><code>
33010 var reader = new Roo.ReaderLayout();
33011 var CP = Roo.ContentPanel;  // shortcut for adding
33012
33013 reader.beginUpdate();
33014 reader.add("north", new CP("north", "North"));
33015 reader.add("west", new CP("west", {title: "West"}));
33016 reader.add("east", new CP("east", {title: "East"}));
33017
33018 reader.regions.listView.add(new CP("listView", "List"));
33019 reader.regions.preview.add(new CP("preview", "Preview"));
33020 reader.endUpdate();
33021 </code></pre>
33022 * @constructor
33023 * Create a new ReaderLayout
33024 * @param {Object} config Configuration options
33025 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
33026 * document.body if omitted)
33027 */
33028 Roo.ReaderLayout = function(config, renderTo){
33029     var c = config || {size:{}};
33030     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
33031         north: c.north !== false ? Roo.apply({
33032             split:false,
33033             initialSize: 32,
33034             titlebar: false
33035         }, c.north) : false,
33036         west: c.west !== false ? Roo.apply({
33037             split:true,
33038             initialSize: 200,
33039             minSize: 175,
33040             maxSize: 400,
33041             titlebar: true,
33042             collapsible: true,
33043             animate: true,
33044             margins:{left:5,right:0,bottom:5,top:5},
33045             cmargins:{left:5,right:5,bottom:5,top:5}
33046         }, c.west) : false,
33047         east: c.east !== false ? Roo.apply({
33048             split:true,
33049             initialSize: 200,
33050             minSize: 175,
33051             maxSize: 400,
33052             titlebar: true,
33053             collapsible: true,
33054             animate: true,
33055             margins:{left:0,right:5,bottom:5,top:5},
33056             cmargins:{left:5,right:5,bottom:5,top:5}
33057         }, c.east) : false,
33058         center: Roo.apply({
33059             tabPosition: 'top',
33060             autoScroll:false,
33061             closeOnTab: true,
33062             titlebar:false,
33063             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
33064         }, c.center)
33065     });
33066
33067     this.el.addClass('x-reader');
33068
33069     this.beginUpdate();
33070
33071     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
33072         south: c.preview !== false ? Roo.apply({
33073             split:true,
33074             initialSize: 200,
33075             minSize: 100,
33076             autoScroll:true,
33077             collapsible:true,
33078             titlebar: true,
33079             cmargins:{top:5,left:0, right:0, bottom:0}
33080         }, c.preview) : false,
33081         center: Roo.apply({
33082             autoScroll:false,
33083             titlebar:false,
33084             minHeight:200
33085         }, c.listView)
33086     });
33087     this.add('center', new Roo.NestedLayoutPanel(inner,
33088             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
33089
33090     this.endUpdate();
33091
33092     this.regions.preview = inner.getRegion('south');
33093     this.regions.listView = inner.getRegion('center');
33094 };
33095
33096 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
33097  * Based on:
33098  * Ext JS Library 1.1.1
33099  * Copyright(c) 2006-2007, Ext JS, LLC.
33100  *
33101  * Originally Released Under LGPL - original licence link has changed is not relivant.
33102  *
33103  * Fork - LGPL
33104  * <script type="text/javascript">
33105  */
33106  
33107 /**
33108  * @class Roo.grid.Grid
33109  * @extends Roo.util.Observable
33110  * This class represents the primary interface of a component based grid control.
33111  * <br><br>Usage:<pre><code>
33112  var grid = new Roo.grid.Grid("my-container-id", {
33113      ds: myDataStore,
33114      cm: myColModel,
33115      selModel: mySelectionModel,
33116      autoSizeColumns: true,
33117      monitorWindowResize: false,
33118      trackMouseOver: true
33119  });
33120  // set any options
33121  grid.render();
33122  * </code></pre>
33123  * <b>Common Problems:</b><br/>
33124  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
33125  * element will correct this<br/>
33126  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
33127  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
33128  * are unpredictable.<br/>
33129  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
33130  * grid to calculate dimensions/offsets.<br/>
33131   * @constructor
33132  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
33133  * The container MUST have some type of size defined for the grid to fill. The container will be
33134  * automatically set to position relative if it isn't already.
33135  * @param {Object} config A config object that sets properties on this grid.
33136  */
33137 Roo.grid.Grid = function(container, config){
33138         // initialize the container
33139         this.container = Roo.get(container);
33140         this.container.update("");
33141         this.container.setStyle("overflow", "hidden");
33142     this.container.addClass('x-grid-container');
33143
33144     this.id = this.container.id;
33145
33146     Roo.apply(this, config);
33147     // check and correct shorthanded configs
33148     if(this.ds){
33149         this.dataSource = this.ds;
33150         delete this.ds;
33151     }
33152     if(this.cm){
33153         this.colModel = this.cm;
33154         delete this.cm;
33155     }
33156     if(this.sm){
33157         this.selModel = this.sm;
33158         delete this.sm;
33159     }
33160
33161     if (this.selModel) {
33162         this.selModel = Roo.factory(this.selModel, Roo.grid);
33163         this.sm = this.selModel;
33164         this.sm.xmodule = this.xmodule || false;
33165     }
33166     if (typeof(this.colModel.config) == 'undefined') {
33167         this.colModel = new Roo.grid.ColumnModel(this.colModel);
33168         this.cm = this.colModel;
33169         this.cm.xmodule = this.xmodule || false;
33170     }
33171     if (this.dataSource) {
33172         this.dataSource= Roo.factory(this.dataSource, Roo.data);
33173         this.ds = this.dataSource;
33174         this.ds.xmodule = this.xmodule || false;
33175          
33176     }
33177     
33178     
33179     
33180     if(this.width){
33181         this.container.setWidth(this.width);
33182     }
33183
33184     if(this.height){
33185         this.container.setHeight(this.height);
33186     }
33187     /** @private */
33188         this.addEvents({
33189         // raw events
33190         /**
33191          * @event click
33192          * The raw click event for the entire grid.
33193          * @param {Roo.EventObject} e
33194          */
33195         "click" : true,
33196         /**
33197          * @event dblclick
33198          * The raw dblclick event for the entire grid.
33199          * @param {Roo.EventObject} e
33200          */
33201         "dblclick" : true,
33202         /**
33203          * @event contextmenu
33204          * The raw contextmenu event for the entire grid.
33205          * @param {Roo.EventObject} e
33206          */
33207         "contextmenu" : true,
33208         /**
33209          * @event mousedown
33210          * The raw mousedown event for the entire grid.
33211          * @param {Roo.EventObject} e
33212          */
33213         "mousedown" : true,
33214         /**
33215          * @event mouseup
33216          * The raw mouseup event for the entire grid.
33217          * @param {Roo.EventObject} e
33218          */
33219         "mouseup" : true,
33220         /**
33221          * @event mouseover
33222          * The raw mouseover event for the entire grid.
33223          * @param {Roo.EventObject} e
33224          */
33225         "mouseover" : true,
33226         /**
33227          * @event mouseout
33228          * The raw mouseout event for the entire grid.
33229          * @param {Roo.EventObject} e
33230          */
33231         "mouseout" : true,
33232         /**
33233          * @event keypress
33234          * The raw keypress event for the entire grid.
33235          * @param {Roo.EventObject} e
33236          */
33237         "keypress" : true,
33238         /**
33239          * @event keydown
33240          * The raw keydown event for the entire grid.
33241          * @param {Roo.EventObject} e
33242          */
33243         "keydown" : true,
33244
33245         // custom events
33246
33247         /**
33248          * @event cellclick
33249          * Fires when a cell is clicked
33250          * @param {Grid} this
33251          * @param {Number} rowIndex
33252          * @param {Number} columnIndex
33253          * @param {Roo.EventObject} e
33254          */
33255         "cellclick" : true,
33256         /**
33257          * @event celldblclick
33258          * Fires when a cell is double clicked
33259          * @param {Grid} this
33260          * @param {Number} rowIndex
33261          * @param {Number} columnIndex
33262          * @param {Roo.EventObject} e
33263          */
33264         "celldblclick" : true,
33265         /**
33266          * @event rowclick
33267          * Fires when a row is clicked
33268          * @param {Grid} this
33269          * @param {Number} rowIndex
33270          * @param {Roo.EventObject} e
33271          */
33272         "rowclick" : true,
33273         /**
33274          * @event rowdblclick
33275          * Fires when a row is double clicked
33276          * @param {Grid} this
33277          * @param {Number} rowIndex
33278          * @param {Roo.EventObject} e
33279          */
33280         "rowdblclick" : true,
33281         /**
33282          * @event headerclick
33283          * Fires when a header is clicked
33284          * @param {Grid} this
33285          * @param {Number} columnIndex
33286          * @param {Roo.EventObject} e
33287          */
33288         "headerclick" : true,
33289         /**
33290          * @event headerdblclick
33291          * Fires when a header cell is double clicked
33292          * @param {Grid} this
33293          * @param {Number} columnIndex
33294          * @param {Roo.EventObject} e
33295          */
33296         "headerdblclick" : true,
33297         /**
33298          * @event rowcontextmenu
33299          * Fires when a row is right clicked
33300          * @param {Grid} this
33301          * @param {Number} rowIndex
33302          * @param {Roo.EventObject} e
33303          */
33304         "rowcontextmenu" : true,
33305         /**
33306          * @event cellcontextmenu
33307          * Fires when a cell is right clicked
33308          * @param {Grid} this
33309          * @param {Number} rowIndex
33310          * @param {Number} cellIndex
33311          * @param {Roo.EventObject} e
33312          */
33313          "cellcontextmenu" : true,
33314         /**
33315          * @event headercontextmenu
33316          * Fires when a header is right clicked
33317          * @param {Grid} this
33318          * @param {Number} columnIndex
33319          * @param {Roo.EventObject} e
33320          */
33321         "headercontextmenu" : true,
33322         /**
33323          * @event bodyscroll
33324          * Fires when the body element is scrolled
33325          * @param {Number} scrollLeft
33326          * @param {Number} scrollTop
33327          */
33328         "bodyscroll" : true,
33329         /**
33330          * @event columnresize
33331          * Fires when the user resizes a column
33332          * @param {Number} columnIndex
33333          * @param {Number} newSize
33334          */
33335         "columnresize" : true,
33336         /**
33337          * @event columnmove
33338          * Fires when the user moves a column
33339          * @param {Number} oldIndex
33340          * @param {Number} newIndex
33341          */
33342         "columnmove" : true,
33343         /**
33344          * @event startdrag
33345          * Fires when row(s) start being dragged
33346          * @param {Grid} this
33347          * @param {Roo.GridDD} dd The drag drop object
33348          * @param {event} e The raw browser event
33349          */
33350         "startdrag" : true,
33351         /**
33352          * @event enddrag
33353          * Fires when a drag operation is complete
33354          * @param {Grid} this
33355          * @param {Roo.GridDD} dd The drag drop object
33356          * @param {event} e The raw browser event
33357          */
33358         "enddrag" : true,
33359         /**
33360          * @event dragdrop
33361          * Fires when dragged row(s) are dropped on a valid DD target
33362          * @param {Grid} this
33363          * @param {Roo.GridDD} dd The drag drop object
33364          * @param {String} targetId The target drag drop object
33365          * @param {event} e The raw browser event
33366          */
33367         "dragdrop" : true,
33368         /**
33369          * @event dragover
33370          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
33371          * @param {Grid} this
33372          * @param {Roo.GridDD} dd The drag drop object
33373          * @param {String} targetId The target drag drop object
33374          * @param {event} e The raw browser event
33375          */
33376         "dragover" : true,
33377         /**
33378          * @event dragenter
33379          *  Fires when the dragged row(s) first cross another DD target while being dragged
33380          * @param {Grid} this
33381          * @param {Roo.GridDD} dd The drag drop object
33382          * @param {String} targetId The target drag drop object
33383          * @param {event} e The raw browser event
33384          */
33385         "dragenter" : true,
33386         /**
33387          * @event dragout
33388          * Fires when the dragged row(s) leave another DD target while being dragged
33389          * @param {Grid} this
33390          * @param {Roo.GridDD} dd The drag drop object
33391          * @param {String} targetId The target drag drop object
33392          * @param {event} e The raw browser event
33393          */
33394         "dragout" : true,
33395         /**
33396          * @event rowclass
33397          * Fires when a row is rendered, so you can change add a style to it.
33398          * @param {GridView} gridview   The grid view
33399          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
33400          */
33401         'rowclass' : true,
33402
33403         /**
33404          * @event render
33405          * Fires when the grid is rendered
33406          * @param {Grid} grid
33407          */
33408         'render' : true
33409     });
33410
33411     Roo.grid.Grid.superclass.constructor.call(this);
33412 };
33413 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
33414     
33415     /**
33416      * @cfg {String} ddGroup - drag drop group.
33417      */
33418
33419     /**
33420      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
33421      */
33422     minColumnWidth : 25,
33423
33424     /**
33425      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
33426      * <b>on initial render.</b> It is more efficient to explicitly size the columns
33427      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
33428      */
33429     autoSizeColumns : false,
33430
33431     /**
33432      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
33433      */
33434     autoSizeHeaders : true,
33435
33436     /**
33437      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
33438      */
33439     monitorWindowResize : true,
33440
33441     /**
33442      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
33443      * rows measured to get a columns size. Default is 0 (all rows).
33444      */
33445     maxRowsToMeasure : 0,
33446
33447     /**
33448      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
33449      */
33450     trackMouseOver : true,
33451
33452     /**
33453     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
33454     */
33455     
33456     /**
33457     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
33458     */
33459     enableDragDrop : false,
33460     
33461     /**
33462     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
33463     */
33464     enableColumnMove : true,
33465     
33466     /**
33467     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
33468     */
33469     enableColumnHide : true,
33470     
33471     /**
33472     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
33473     */
33474     enableRowHeightSync : false,
33475     
33476     /**
33477     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
33478     */
33479     stripeRows : true,
33480     
33481     /**
33482     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
33483     */
33484     autoHeight : false,
33485
33486     /**
33487      * @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.
33488      */
33489     autoExpandColumn : false,
33490
33491     /**
33492     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
33493     * Default is 50.
33494     */
33495     autoExpandMin : 50,
33496
33497     /**
33498     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
33499     */
33500     autoExpandMax : 1000,
33501
33502     /**
33503     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
33504     */
33505     view : null,
33506
33507     /**
33508     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
33509     */
33510     loadMask : false,
33511     /**
33512     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
33513     */
33514     dropTarget: false,
33515     
33516    
33517     
33518     // private
33519     rendered : false,
33520
33521     /**
33522     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
33523     * of a fixed width. Default is false.
33524     */
33525     /**
33526     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
33527     */
33528     /**
33529      * Called once after all setup has been completed and the grid is ready to be rendered.
33530      * @return {Roo.grid.Grid} this
33531      */
33532     render : function()
33533     {
33534         var c = this.container;
33535         // try to detect autoHeight/width mode
33536         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
33537             this.autoHeight = true;
33538         }
33539         var view = this.getView();
33540         view.init(this);
33541
33542         c.on("click", this.onClick, this);
33543         c.on("dblclick", this.onDblClick, this);
33544         c.on("contextmenu", this.onContextMenu, this);
33545         c.on("keydown", this.onKeyDown, this);
33546
33547         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
33548
33549         this.getSelectionModel().init(this);
33550
33551         view.render();
33552
33553         if(this.loadMask){
33554             this.loadMask = new Roo.LoadMask(this.container,
33555                     Roo.apply({store:this.dataSource}, this.loadMask));
33556         }
33557         
33558         
33559         if (this.toolbar && this.toolbar.xtype) {
33560             this.toolbar.container = this.getView().getHeaderPanel(true);
33561             this.toolbar = new Roo.Toolbar(this.toolbar);
33562         }
33563         if (this.footer && this.footer.xtype) {
33564             this.footer.dataSource = this.getDataSource();
33565             this.footer.container = this.getView().getFooterPanel(true);
33566             this.footer = Roo.factory(this.footer, Roo);
33567         }
33568         if (this.dropTarget && this.dropTarget.xtype) {
33569             delete this.dropTarget.xtype;
33570             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
33571         }
33572         
33573         
33574         this.rendered = true;
33575         this.fireEvent('render', this);
33576         return this;
33577     },
33578
33579         /**
33580          * Reconfigures the grid to use a different Store and Column Model.
33581          * The View will be bound to the new objects and refreshed.
33582          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
33583          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
33584          */
33585     reconfigure : function(dataSource, colModel){
33586         if(this.loadMask){
33587             this.loadMask.destroy();
33588             this.loadMask = new Roo.LoadMask(this.container,
33589                     Roo.apply({store:dataSource}, this.loadMask));
33590         }
33591         this.view.bind(dataSource, colModel);
33592         this.dataSource = dataSource;
33593         this.colModel = colModel;
33594         this.view.refresh(true);
33595     },
33596
33597     // private
33598     onKeyDown : function(e){
33599         this.fireEvent("keydown", e);
33600     },
33601
33602     /**
33603      * Destroy this grid.
33604      * @param {Boolean} removeEl True to remove the element
33605      */
33606     destroy : function(removeEl, keepListeners){
33607         if(this.loadMask){
33608             this.loadMask.destroy();
33609         }
33610         var c = this.container;
33611         c.removeAllListeners();
33612         this.view.destroy();
33613         this.colModel.purgeListeners();
33614         if(!keepListeners){
33615             this.purgeListeners();
33616         }
33617         c.update("");
33618         if(removeEl === true){
33619             c.remove();
33620         }
33621     },
33622
33623     // private
33624     processEvent : function(name, e){
33625         this.fireEvent(name, e);
33626         var t = e.getTarget();
33627         var v = this.view;
33628         var header = v.findHeaderIndex(t);
33629         if(header !== false){
33630             this.fireEvent("header" + name, this, header, e);
33631         }else{
33632             var row = v.findRowIndex(t);
33633             var cell = v.findCellIndex(t);
33634             if(row !== false){
33635                 this.fireEvent("row" + name, this, row, e);
33636                 if(cell !== false){
33637                     this.fireEvent("cell" + name, this, row, cell, e);
33638                 }
33639             }
33640         }
33641     },
33642
33643     // private
33644     onClick : function(e){
33645         this.processEvent("click", e);
33646     },
33647
33648     // private
33649     onContextMenu : function(e, t){
33650         this.processEvent("contextmenu", e);
33651     },
33652
33653     // private
33654     onDblClick : function(e){
33655         this.processEvent("dblclick", e);
33656     },
33657
33658     // private
33659     walkCells : function(row, col, step, fn, scope){
33660         var cm = this.colModel, clen = cm.getColumnCount();
33661         var ds = this.dataSource, rlen = ds.getCount(), first = true;
33662         if(step < 0){
33663             if(col < 0){
33664                 row--;
33665                 first = false;
33666             }
33667             while(row >= 0){
33668                 if(!first){
33669                     col = clen-1;
33670                 }
33671                 first = false;
33672                 while(col >= 0){
33673                     if(fn.call(scope || this, row, col, cm) === true){
33674                         return [row, col];
33675                     }
33676                     col--;
33677                 }
33678                 row--;
33679             }
33680         } else {
33681             if(col >= clen){
33682                 row++;
33683                 first = false;
33684             }
33685             while(row < rlen){
33686                 if(!first){
33687                     col = 0;
33688                 }
33689                 first = false;
33690                 while(col < clen){
33691                     if(fn.call(scope || this, row, col, cm) === true){
33692                         return [row, col];
33693                     }
33694                     col++;
33695                 }
33696                 row++;
33697             }
33698         }
33699         return null;
33700     },
33701
33702     // private
33703     getSelections : function(){
33704         return this.selModel.getSelections();
33705     },
33706
33707     /**
33708      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
33709      * but if manual update is required this method will initiate it.
33710      */
33711     autoSize : function(){
33712         if(this.rendered){
33713             this.view.layout();
33714             if(this.view.adjustForScroll){
33715                 this.view.adjustForScroll();
33716             }
33717         }
33718     },
33719
33720     /**
33721      * Returns the grid's underlying element.
33722      * @return {Element} The element
33723      */
33724     getGridEl : function(){
33725         return this.container;
33726     },
33727
33728     // private for compatibility, overridden by editor grid
33729     stopEditing : function(){},
33730
33731     /**
33732      * Returns the grid's SelectionModel.
33733      * @return {SelectionModel}
33734      */
33735     getSelectionModel : function(){
33736         if(!this.selModel){
33737             this.selModel = new Roo.grid.RowSelectionModel();
33738         }
33739         return this.selModel;
33740     },
33741
33742     /**
33743      * Returns the grid's DataSource.
33744      * @return {DataSource}
33745      */
33746     getDataSource : function(){
33747         return this.dataSource;
33748     },
33749
33750     /**
33751      * Returns the grid's ColumnModel.
33752      * @return {ColumnModel}
33753      */
33754     getColumnModel : function(){
33755         return this.colModel;
33756     },
33757
33758     /**
33759      * Returns the grid's GridView object.
33760      * @return {GridView}
33761      */
33762     getView : function(){
33763         if(!this.view){
33764             this.view = new Roo.grid.GridView(this.viewConfig);
33765         }
33766         return this.view;
33767     },
33768     /**
33769      * Called to get grid's drag proxy text, by default returns this.ddText.
33770      * @return {String}
33771      */
33772     getDragDropText : function(){
33773         var count = this.selModel.getCount();
33774         return String.format(this.ddText, count, count == 1 ? '' : 's');
33775     }
33776 });
33777 /**
33778  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
33779  * %0 is replaced with the number of selected rows.
33780  * @type String
33781  */
33782 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
33783  * Based on:
33784  * Ext JS Library 1.1.1
33785  * Copyright(c) 2006-2007, Ext JS, LLC.
33786  *
33787  * Originally Released Under LGPL - original licence link has changed is not relivant.
33788  *
33789  * Fork - LGPL
33790  * <script type="text/javascript">
33791  */
33792  
33793 Roo.grid.AbstractGridView = function(){
33794         this.grid = null;
33795         
33796         this.events = {
33797             "beforerowremoved" : true,
33798             "beforerowsinserted" : true,
33799             "beforerefresh" : true,
33800             "rowremoved" : true,
33801             "rowsinserted" : true,
33802             "rowupdated" : true,
33803             "refresh" : true
33804         };
33805     Roo.grid.AbstractGridView.superclass.constructor.call(this);
33806 };
33807
33808 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
33809     rowClass : "x-grid-row",
33810     cellClass : "x-grid-cell",
33811     tdClass : "x-grid-td",
33812     hdClass : "x-grid-hd",
33813     splitClass : "x-grid-hd-split",
33814     
33815         init: function(grid){
33816         this.grid = grid;
33817                 var cid = this.grid.getGridEl().id;
33818         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33819         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33820         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33821         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33822         },
33823         
33824         getColumnRenderers : function(){
33825         var renderers = [];
33826         var cm = this.grid.colModel;
33827         var colCount = cm.getColumnCount();
33828         for(var i = 0; i < colCount; i++){
33829             renderers[i] = cm.getRenderer(i);
33830         }
33831         return renderers;
33832     },
33833     
33834     getColumnIds : function(){
33835         var ids = [];
33836         var cm = this.grid.colModel;
33837         var colCount = cm.getColumnCount();
33838         for(var i = 0; i < colCount; i++){
33839             ids[i] = cm.getColumnId(i);
33840         }
33841         return ids;
33842     },
33843     
33844     getDataIndexes : function(){
33845         if(!this.indexMap){
33846             this.indexMap = this.buildIndexMap();
33847         }
33848         return this.indexMap.colToData;
33849     },
33850     
33851     getColumnIndexByDataIndex : function(dataIndex){
33852         if(!this.indexMap){
33853             this.indexMap = this.buildIndexMap();
33854         }
33855         return this.indexMap.dataToCol[dataIndex];
33856     },
33857     
33858     /**
33859      * Set a css style for a column dynamically. 
33860      * @param {Number} colIndex The index of the column
33861      * @param {String} name The css property name
33862      * @param {String} value The css value
33863      */
33864     setCSSStyle : function(colIndex, name, value){
33865         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33866         Roo.util.CSS.updateRule(selector, name, value);
33867     },
33868     
33869     generateRules : function(cm){
33870         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33871         Roo.util.CSS.removeStyleSheet(rulesId);
33872         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33873             var cid = cm.getColumnId(i);
33874             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33875                          this.tdSelector, cid, " {\n}\n",
33876                          this.hdSelector, cid, " {\n}\n",
33877                          this.splitSelector, cid, " {\n}\n");
33878         }
33879         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33880     }
33881 });/*
33882  * Based on:
33883  * Ext JS Library 1.1.1
33884  * Copyright(c) 2006-2007, Ext JS, LLC.
33885  *
33886  * Originally Released Under LGPL - original licence link has changed is not relivant.
33887  *
33888  * Fork - LGPL
33889  * <script type="text/javascript">
33890  */
33891
33892 // private
33893 // This is a support class used internally by the Grid components
33894 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33895     this.grid = grid;
33896     this.view = grid.getView();
33897     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33898     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33899     if(hd2){
33900         this.setHandleElId(Roo.id(hd));
33901         this.setOuterHandleElId(Roo.id(hd2));
33902     }
33903     this.scroll = false;
33904 };
33905 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33906     maxDragWidth: 120,
33907     getDragData : function(e){
33908         var t = Roo.lib.Event.getTarget(e);
33909         var h = this.view.findHeaderCell(t);
33910         if(h){
33911             return {ddel: h.firstChild, header:h};
33912         }
33913         return false;
33914     },
33915
33916     onInitDrag : function(e){
33917         this.view.headersDisabled = true;
33918         var clone = this.dragData.ddel.cloneNode(true);
33919         clone.id = Roo.id();
33920         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33921         this.proxy.update(clone);
33922         return true;
33923     },
33924
33925     afterValidDrop : function(){
33926         var v = this.view;
33927         setTimeout(function(){
33928             v.headersDisabled = false;
33929         }, 50);
33930     },
33931
33932     afterInvalidDrop : function(){
33933         var v = this.view;
33934         setTimeout(function(){
33935             v.headersDisabled = false;
33936         }, 50);
33937     }
33938 });
33939 /*
33940  * Based on:
33941  * Ext JS Library 1.1.1
33942  * Copyright(c) 2006-2007, Ext JS, LLC.
33943  *
33944  * Originally Released Under LGPL - original licence link has changed is not relivant.
33945  *
33946  * Fork - LGPL
33947  * <script type="text/javascript">
33948  */
33949 // private
33950 // This is a support class used internally by the Grid components
33951 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33952     this.grid = grid;
33953     this.view = grid.getView();
33954     // split the proxies so they don't interfere with mouse events
33955     this.proxyTop = Roo.DomHelper.append(document.body, {
33956         cls:"col-move-top", html:"&#160;"
33957     }, true);
33958     this.proxyBottom = Roo.DomHelper.append(document.body, {
33959         cls:"col-move-bottom", html:"&#160;"
33960     }, true);
33961     this.proxyTop.hide = this.proxyBottom.hide = function(){
33962         this.setLeftTop(-100,-100);
33963         this.setStyle("visibility", "hidden");
33964     };
33965     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33966     // temporarily disabled
33967     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33968     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33969 };
33970 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33971     proxyOffsets : [-4, -9],
33972     fly: Roo.Element.fly,
33973
33974     getTargetFromEvent : function(e){
33975         var t = Roo.lib.Event.getTarget(e);
33976         var cindex = this.view.findCellIndex(t);
33977         if(cindex !== false){
33978             return this.view.getHeaderCell(cindex);
33979         }
33980         return null;
33981     },
33982
33983     nextVisible : function(h){
33984         var v = this.view, cm = this.grid.colModel;
33985         h = h.nextSibling;
33986         while(h){
33987             if(!cm.isHidden(v.getCellIndex(h))){
33988                 return h;
33989             }
33990             h = h.nextSibling;
33991         }
33992         return null;
33993     },
33994
33995     prevVisible : function(h){
33996         var v = this.view, cm = this.grid.colModel;
33997         h = h.prevSibling;
33998         while(h){
33999             if(!cm.isHidden(v.getCellIndex(h))){
34000                 return h;
34001             }
34002             h = h.prevSibling;
34003         }
34004         return null;
34005     },
34006
34007     positionIndicator : function(h, n, e){
34008         var x = Roo.lib.Event.getPageX(e);
34009         var r = Roo.lib.Dom.getRegion(n.firstChild);
34010         var px, pt, py = r.top + this.proxyOffsets[1];
34011         if((r.right - x) <= (r.right-r.left)/2){
34012             px = r.right+this.view.borderWidth;
34013             pt = "after";
34014         }else{
34015             px = r.left;
34016             pt = "before";
34017         }
34018         var oldIndex = this.view.getCellIndex(h);
34019         var newIndex = this.view.getCellIndex(n);
34020
34021         if(this.grid.colModel.isFixed(newIndex)){
34022             return false;
34023         }
34024
34025         var locked = this.grid.colModel.isLocked(newIndex);
34026
34027         if(pt == "after"){
34028             newIndex++;
34029         }
34030         if(oldIndex < newIndex){
34031             newIndex--;
34032         }
34033         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
34034             return false;
34035         }
34036         px +=  this.proxyOffsets[0];
34037         this.proxyTop.setLeftTop(px, py);
34038         this.proxyTop.show();
34039         if(!this.bottomOffset){
34040             this.bottomOffset = this.view.mainHd.getHeight();
34041         }
34042         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
34043         this.proxyBottom.show();
34044         return pt;
34045     },
34046
34047     onNodeEnter : function(n, dd, e, data){
34048         if(data.header != n){
34049             this.positionIndicator(data.header, n, e);
34050         }
34051     },
34052
34053     onNodeOver : function(n, dd, e, data){
34054         var result = false;
34055         if(data.header != n){
34056             result = this.positionIndicator(data.header, n, e);
34057         }
34058         if(!result){
34059             this.proxyTop.hide();
34060             this.proxyBottom.hide();
34061         }
34062         return result ? this.dropAllowed : this.dropNotAllowed;
34063     },
34064
34065     onNodeOut : function(n, dd, e, data){
34066         this.proxyTop.hide();
34067         this.proxyBottom.hide();
34068     },
34069
34070     onNodeDrop : function(n, dd, e, data){
34071         var h = data.header;
34072         if(h != n){
34073             var cm = this.grid.colModel;
34074             var x = Roo.lib.Event.getPageX(e);
34075             var r = Roo.lib.Dom.getRegion(n.firstChild);
34076             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
34077             var oldIndex = this.view.getCellIndex(h);
34078             var newIndex = this.view.getCellIndex(n);
34079             var locked = cm.isLocked(newIndex);
34080             if(pt == "after"){
34081                 newIndex++;
34082             }
34083             if(oldIndex < newIndex){
34084                 newIndex--;
34085             }
34086             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
34087                 return false;
34088             }
34089             cm.setLocked(oldIndex, locked, true);
34090             cm.moveColumn(oldIndex, newIndex);
34091             this.grid.fireEvent("columnmove", oldIndex, newIndex);
34092             return true;
34093         }
34094         return false;
34095     }
34096 });
34097 /*
34098  * Based on:
34099  * Ext JS Library 1.1.1
34100  * Copyright(c) 2006-2007, Ext JS, LLC.
34101  *
34102  * Originally Released Under LGPL - original licence link has changed is not relivant.
34103  *
34104  * Fork - LGPL
34105  * <script type="text/javascript">
34106  */
34107   
34108 /**
34109  * @class Roo.grid.GridView
34110  * @extends Roo.util.Observable
34111  *
34112  * @constructor
34113  * @param {Object} config
34114  */
34115 Roo.grid.GridView = function(config){
34116     Roo.grid.GridView.superclass.constructor.call(this);
34117     this.el = null;
34118
34119     Roo.apply(this, config);
34120 };
34121
34122 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
34123
34124     
34125     rowClass : "x-grid-row",
34126
34127     cellClass : "x-grid-col",
34128
34129     tdClass : "x-grid-td",
34130
34131     hdClass : "x-grid-hd",
34132
34133     splitClass : "x-grid-split",
34134
34135     sortClasses : ["sort-asc", "sort-desc"],
34136
34137     enableMoveAnim : false,
34138
34139     hlColor: "C3DAF9",
34140
34141     dh : Roo.DomHelper,
34142
34143     fly : Roo.Element.fly,
34144
34145     css : Roo.util.CSS,
34146
34147     borderWidth: 1,
34148
34149     splitOffset: 3,
34150
34151     scrollIncrement : 22,
34152
34153     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
34154
34155     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
34156
34157     bind : function(ds, cm){
34158         if(this.ds){
34159             this.ds.un("load", this.onLoad, this);
34160             this.ds.un("datachanged", this.onDataChange, this);
34161             this.ds.un("add", this.onAdd, this);
34162             this.ds.un("remove", this.onRemove, this);
34163             this.ds.un("update", this.onUpdate, this);
34164             this.ds.un("clear", this.onClear, this);
34165         }
34166         if(ds){
34167             ds.on("load", this.onLoad, this);
34168             ds.on("datachanged", this.onDataChange, this);
34169             ds.on("add", this.onAdd, this);
34170             ds.on("remove", this.onRemove, this);
34171             ds.on("update", this.onUpdate, this);
34172             ds.on("clear", this.onClear, this);
34173         }
34174         this.ds = ds;
34175
34176         if(this.cm){
34177             this.cm.un("widthchange", this.onColWidthChange, this);
34178             this.cm.un("headerchange", this.onHeaderChange, this);
34179             this.cm.un("hiddenchange", this.onHiddenChange, this);
34180             this.cm.un("columnmoved", this.onColumnMove, this);
34181             this.cm.un("columnlockchange", this.onColumnLock, this);
34182         }
34183         if(cm){
34184             this.generateRules(cm);
34185             cm.on("widthchange", this.onColWidthChange, this);
34186             cm.on("headerchange", this.onHeaderChange, this);
34187             cm.on("hiddenchange", this.onHiddenChange, this);
34188             cm.on("columnmoved", this.onColumnMove, this);
34189             cm.on("columnlockchange", this.onColumnLock, this);
34190         }
34191         this.cm = cm;
34192     },
34193
34194     init: function(grid){
34195         Roo.grid.GridView.superclass.init.call(this, grid);
34196
34197         this.bind(grid.dataSource, grid.colModel);
34198
34199         grid.on("headerclick", this.handleHeaderClick, this);
34200
34201         if(grid.trackMouseOver){
34202             grid.on("mouseover", this.onRowOver, this);
34203             grid.on("mouseout", this.onRowOut, this);
34204         }
34205         grid.cancelTextSelection = function(){};
34206         this.gridId = grid.id;
34207
34208         var tpls = this.templates || {};
34209
34210         if(!tpls.master){
34211             tpls.master = new Roo.Template(
34212                '<div class="x-grid" hidefocus="true">',
34213                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
34214                   '<div class="x-grid-topbar"></div>',
34215                   '<div class="x-grid-scroller"><div></div></div>',
34216                   '<div class="x-grid-locked">',
34217                       '<div class="x-grid-header">{lockedHeader}</div>',
34218                       '<div class="x-grid-body">{lockedBody}</div>',
34219                   "</div>",
34220                   '<div class="x-grid-viewport">',
34221                       '<div class="x-grid-header">{header}</div>',
34222                       '<div class="x-grid-body">{body}</div>',
34223                   "</div>",
34224                   '<div class="x-grid-bottombar"></div>',
34225                  
34226                   '<div class="x-grid-resize-proxy">&#160;</div>',
34227                "</div>"
34228             );
34229             tpls.master.disableformats = true;
34230         }
34231
34232         if(!tpls.header){
34233             tpls.header = new Roo.Template(
34234                '<table border="0" cellspacing="0" cellpadding="0">',
34235                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
34236                "</table>{splits}"
34237             );
34238             tpls.header.disableformats = true;
34239         }
34240         tpls.header.compile();
34241
34242         if(!tpls.hcell){
34243             tpls.hcell = new Roo.Template(
34244                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
34245                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
34246                 "</div></td>"
34247              );
34248              tpls.hcell.disableFormats = true;
34249         }
34250         tpls.hcell.compile();
34251
34252         if(!tpls.hsplit){
34253             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
34254             tpls.hsplit.disableFormats = true;
34255         }
34256         tpls.hsplit.compile();
34257
34258         if(!tpls.body){
34259             tpls.body = new Roo.Template(
34260                '<table border="0" cellspacing="0" cellpadding="0">',
34261                "<tbody>{rows}</tbody>",
34262                "</table>"
34263             );
34264             tpls.body.disableFormats = true;
34265         }
34266         tpls.body.compile();
34267
34268         if(!tpls.row){
34269             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
34270             tpls.row.disableFormats = true;
34271         }
34272         tpls.row.compile();
34273
34274         if(!tpls.cell){
34275             tpls.cell = new Roo.Template(
34276                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
34277                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
34278                 "</td>"
34279             );
34280             tpls.cell.disableFormats = true;
34281         }
34282         tpls.cell.compile();
34283
34284         this.templates = tpls;
34285     },
34286
34287     // remap these for backwards compat
34288     onColWidthChange : function(){
34289         this.updateColumns.apply(this, arguments);
34290     },
34291     onHeaderChange : function(){
34292         this.updateHeaders.apply(this, arguments);
34293     }, 
34294     onHiddenChange : function(){
34295         this.handleHiddenChange.apply(this, arguments);
34296     },
34297     onColumnMove : function(){
34298         this.handleColumnMove.apply(this, arguments);
34299     },
34300     onColumnLock : function(){
34301         this.handleLockChange.apply(this, arguments);
34302     },
34303
34304     onDataChange : function(){
34305         this.refresh();
34306         this.updateHeaderSortState();
34307     },
34308
34309     onClear : function(){
34310         this.refresh();
34311     },
34312
34313     onUpdate : function(ds, record){
34314         this.refreshRow(record);
34315     },
34316
34317     refreshRow : function(record){
34318         var ds = this.ds, index;
34319         if(typeof record == 'number'){
34320             index = record;
34321             record = ds.getAt(index);
34322         }else{
34323             index = ds.indexOf(record);
34324         }
34325         this.insertRows(ds, index, index, true);
34326         this.onRemove(ds, record, index+1, true);
34327         this.syncRowHeights(index, index);
34328         this.layout();
34329         this.fireEvent("rowupdated", this, index, record);
34330     },
34331
34332     onAdd : function(ds, records, index){
34333         this.insertRows(ds, index, index + (records.length-1));
34334     },
34335
34336     onRemove : function(ds, record, index, isUpdate){
34337         if(isUpdate !== true){
34338             this.fireEvent("beforerowremoved", this, index, record);
34339         }
34340         var bt = this.getBodyTable(), lt = this.getLockedTable();
34341         if(bt.rows[index]){
34342             bt.firstChild.removeChild(bt.rows[index]);
34343         }
34344         if(lt.rows[index]){
34345             lt.firstChild.removeChild(lt.rows[index]);
34346         }
34347         if(isUpdate !== true){
34348             this.stripeRows(index);
34349             this.syncRowHeights(index, index);
34350             this.layout();
34351             this.fireEvent("rowremoved", this, index, record);
34352         }
34353     },
34354
34355     onLoad : function(){
34356         this.scrollToTop();
34357     },
34358
34359     /**
34360      * Scrolls the grid to the top
34361      */
34362     scrollToTop : function(){
34363         if(this.scroller){
34364             this.scroller.dom.scrollTop = 0;
34365             this.syncScroll();
34366         }
34367     },
34368
34369     /**
34370      * Gets a panel in the header of the grid that can be used for toolbars etc.
34371      * After modifying the contents of this panel a call to grid.autoSize() may be
34372      * required to register any changes in size.
34373      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
34374      * @return Roo.Element
34375      */
34376     getHeaderPanel : function(doShow){
34377         if(doShow){
34378             this.headerPanel.show();
34379         }
34380         return this.headerPanel;
34381     },
34382
34383     /**
34384      * Gets a panel in the footer of the grid that can be used for toolbars etc.
34385      * After modifying the contents of this panel a call to grid.autoSize() may be
34386      * required to register any changes in size.
34387      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
34388      * @return Roo.Element
34389      */
34390     getFooterPanel : function(doShow){
34391         if(doShow){
34392             this.footerPanel.show();
34393         }
34394         return this.footerPanel;
34395     },
34396
34397     initElements : function(){
34398         var E = Roo.Element;
34399         var el = this.grid.getGridEl().dom.firstChild;
34400         var cs = el.childNodes;
34401
34402         this.el = new E(el);
34403         
34404          this.focusEl = new E(el.firstChild);
34405         this.focusEl.swallowEvent("click", true);
34406         
34407         this.headerPanel = new E(cs[1]);
34408         this.headerPanel.enableDisplayMode("block");
34409
34410         this.scroller = new E(cs[2]);
34411         this.scrollSizer = new E(this.scroller.dom.firstChild);
34412
34413         this.lockedWrap = new E(cs[3]);
34414         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
34415         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
34416
34417         this.mainWrap = new E(cs[4]);
34418         this.mainHd = new E(this.mainWrap.dom.firstChild);
34419         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
34420
34421         this.footerPanel = new E(cs[5]);
34422         this.footerPanel.enableDisplayMode("block");
34423
34424         this.resizeProxy = new E(cs[6]);
34425
34426         this.headerSelector = String.format(
34427            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
34428            this.lockedHd.id, this.mainHd.id
34429         );
34430
34431         this.splitterSelector = String.format(
34432            '#{0} div.x-grid-split, #{1} div.x-grid-split',
34433            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
34434         );
34435     },
34436     idToCssName : function(s)
34437     {
34438         return s.replace(/[^a-z0-9]+/ig, '-');
34439     },
34440
34441     getHeaderCell : function(index){
34442         return Roo.DomQuery.select(this.headerSelector)[index];
34443     },
34444
34445     getHeaderCellMeasure : function(index){
34446         return this.getHeaderCell(index).firstChild;
34447     },
34448
34449     getHeaderCellText : function(index){
34450         return this.getHeaderCell(index).firstChild.firstChild;
34451     },
34452
34453     getLockedTable : function(){
34454         return this.lockedBody.dom.firstChild;
34455     },
34456
34457     getBodyTable : function(){
34458         return this.mainBody.dom.firstChild;
34459     },
34460
34461     getLockedRow : function(index){
34462         return this.getLockedTable().rows[index];
34463     },
34464
34465     getRow : function(index){
34466         return this.getBodyTable().rows[index];
34467     },
34468
34469     getRowComposite : function(index){
34470         if(!this.rowEl){
34471             this.rowEl = new Roo.CompositeElementLite();
34472         }
34473         var els = [], lrow, mrow;
34474         if(lrow = this.getLockedRow(index)){
34475             els.push(lrow);
34476         }
34477         if(mrow = this.getRow(index)){
34478             els.push(mrow);
34479         }
34480         this.rowEl.elements = els;
34481         return this.rowEl;
34482     },
34483     /**
34484      * Gets the 'td' of the cell
34485      * 
34486      * @param {Integer} rowIndex row to select
34487      * @param {Integer} colIndex column to select
34488      * 
34489      * @return {Object} 
34490      */
34491     getCell : function(rowIndex, colIndex){
34492         var locked = this.cm.getLockedCount();
34493         var source;
34494         if(colIndex < locked){
34495             source = this.lockedBody.dom.firstChild;
34496         }else{
34497             source = this.mainBody.dom.firstChild;
34498             colIndex -= locked;
34499         }
34500         return source.rows[rowIndex].childNodes[colIndex];
34501     },
34502
34503     getCellText : function(rowIndex, colIndex){
34504         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
34505     },
34506
34507     getCellBox : function(cell){
34508         var b = this.fly(cell).getBox();
34509         if(Roo.isOpera){ // opera fails to report the Y
34510             b.y = cell.offsetTop + this.mainBody.getY();
34511         }
34512         return b;
34513     },
34514
34515     getCellIndex : function(cell){
34516         var id = String(cell.className).match(this.cellRE);
34517         if(id){
34518             return parseInt(id[1], 10);
34519         }
34520         return 0;
34521     },
34522
34523     findHeaderIndex : function(n){
34524         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34525         return r ? this.getCellIndex(r) : false;
34526     },
34527
34528     findHeaderCell : function(n){
34529         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34530         return r ? r : false;
34531     },
34532
34533     findRowIndex : function(n){
34534         if(!n){
34535             return false;
34536         }
34537         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
34538         return r ? r.rowIndex : false;
34539     },
34540
34541     findCellIndex : function(node){
34542         var stop = this.el.dom;
34543         while(node && node != stop){
34544             if(this.findRE.test(node.className)){
34545                 return this.getCellIndex(node);
34546             }
34547             node = node.parentNode;
34548         }
34549         return false;
34550     },
34551
34552     getColumnId : function(index){
34553         return this.cm.getColumnId(index);
34554     },
34555
34556     getSplitters : function()
34557     {
34558         if(this.splitterSelector){
34559            return Roo.DomQuery.select(this.splitterSelector);
34560         }else{
34561             return null;
34562       }
34563     },
34564
34565     getSplitter : function(index){
34566         return this.getSplitters()[index];
34567     },
34568
34569     onRowOver : function(e, t){
34570         var row;
34571         if((row = this.findRowIndex(t)) !== false){
34572             this.getRowComposite(row).addClass("x-grid-row-over");
34573         }
34574     },
34575
34576     onRowOut : function(e, t){
34577         var row;
34578         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
34579             this.getRowComposite(row).removeClass("x-grid-row-over");
34580         }
34581     },
34582
34583     renderHeaders : function(){
34584         var cm = this.cm;
34585         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
34586         var cb = [], lb = [], sb = [], lsb = [], p = {};
34587         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34588             p.cellId = "x-grid-hd-0-" + i;
34589             p.splitId = "x-grid-csplit-0-" + i;
34590             p.id = cm.getColumnId(i);
34591             p.title = cm.getColumnTooltip(i) || "";
34592             p.value = cm.getColumnHeader(i) || "";
34593             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
34594             if(!cm.isLocked(i)){
34595                 cb[cb.length] = ct.apply(p);
34596                 sb[sb.length] = st.apply(p);
34597             }else{
34598                 lb[lb.length] = ct.apply(p);
34599                 lsb[lsb.length] = st.apply(p);
34600             }
34601         }
34602         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
34603                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
34604     },
34605
34606     updateHeaders : function(){
34607         var html = this.renderHeaders();
34608         this.lockedHd.update(html[0]);
34609         this.mainHd.update(html[1]);
34610     },
34611
34612     /**
34613      * Focuses the specified row.
34614      * @param {Number} row The row index
34615      */
34616     focusRow : function(row)
34617     {
34618         //Roo.log('GridView.focusRow');
34619         var x = this.scroller.dom.scrollLeft;
34620         this.focusCell(row, 0, false);
34621         this.scroller.dom.scrollLeft = x;
34622     },
34623
34624     /**
34625      * Focuses the specified cell.
34626      * @param {Number} row The row index
34627      * @param {Number} col The column index
34628      * @param {Boolean} hscroll false to disable horizontal scrolling
34629      */
34630     focusCell : function(row, col, hscroll)
34631     {
34632         //Roo.log('GridView.focusCell');
34633         var el = this.ensureVisible(row, col, hscroll);
34634         this.focusEl.alignTo(el, "tl-tl");
34635         if(Roo.isGecko){
34636             this.focusEl.focus();
34637         }else{
34638             this.focusEl.focus.defer(1, this.focusEl);
34639         }
34640     },
34641
34642     /**
34643      * Scrolls the specified cell into view
34644      * @param {Number} row The row index
34645      * @param {Number} col The column index
34646      * @param {Boolean} hscroll false to disable horizontal scrolling
34647      */
34648     ensureVisible : function(row, col, hscroll)
34649     {
34650         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
34651         //return null; //disable for testing.
34652         if(typeof row != "number"){
34653             row = row.rowIndex;
34654         }
34655         if(row < 0 && row >= this.ds.getCount()){
34656             return  null;
34657         }
34658         col = (col !== undefined ? col : 0);
34659         var cm = this.grid.colModel;
34660         while(cm.isHidden(col)){
34661             col++;
34662         }
34663
34664         var el = this.getCell(row, col);
34665         if(!el){
34666             return null;
34667         }
34668         var c = this.scroller.dom;
34669
34670         var ctop = parseInt(el.offsetTop, 10);
34671         var cleft = parseInt(el.offsetLeft, 10);
34672         var cbot = ctop + el.offsetHeight;
34673         var cright = cleft + el.offsetWidth;
34674         
34675         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
34676         var stop = parseInt(c.scrollTop, 10);
34677         var sleft = parseInt(c.scrollLeft, 10);
34678         var sbot = stop + ch;
34679         var sright = sleft + c.clientWidth;
34680         /*
34681         Roo.log('GridView.ensureVisible:' +
34682                 ' ctop:' + ctop +
34683                 ' c.clientHeight:' + c.clientHeight +
34684                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
34685                 ' stop:' + stop +
34686                 ' cbot:' + cbot +
34687                 ' sbot:' + sbot +
34688                 ' ch:' + ch  
34689                 );
34690         */
34691         if(ctop < stop){
34692              c.scrollTop = ctop;
34693             //Roo.log("set scrolltop to ctop DISABLE?");
34694         }else if(cbot > sbot){
34695             //Roo.log("set scrolltop to cbot-ch");
34696             c.scrollTop = cbot-ch;
34697         }
34698         
34699         if(hscroll !== false){
34700             if(cleft < sleft){
34701                 c.scrollLeft = cleft;
34702             }else if(cright > sright){
34703                 c.scrollLeft = cright-c.clientWidth;
34704             }
34705         }
34706          
34707         return el;
34708     },
34709
34710     updateColumns : function(){
34711         this.grid.stopEditing();
34712         var cm = this.grid.colModel, colIds = this.getColumnIds();
34713         //var totalWidth = cm.getTotalWidth();
34714         var pos = 0;
34715         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34716             //if(cm.isHidden(i)) continue;
34717             var w = cm.getColumnWidth(i);
34718             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34719             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34720         }
34721         this.updateSplitters();
34722     },
34723
34724     generateRules : function(cm){
34725         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
34726         Roo.util.CSS.removeStyleSheet(rulesId);
34727         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34728             var cid = cm.getColumnId(i);
34729             var align = '';
34730             if(cm.config[i].align){
34731                 align = 'text-align:'+cm.config[i].align+';';
34732             }
34733             var hidden = '';
34734             if(cm.isHidden(i)){
34735                 hidden = 'display:none;';
34736             }
34737             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
34738             ruleBuf.push(
34739                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
34740                     this.hdSelector, cid, " {\n", align, width, "}\n",
34741                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
34742                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
34743         }
34744         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34745     },
34746
34747     updateSplitters : function(){
34748         var cm = this.cm, s = this.getSplitters();
34749         if(s){ // splitters not created yet
34750             var pos = 0, locked = true;
34751             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34752                 if(cm.isHidden(i)) continue;
34753                 var w = cm.getColumnWidth(i); // make sure it's a number
34754                 if(!cm.isLocked(i) && locked){
34755                     pos = 0;
34756                     locked = false;
34757                 }
34758                 pos += w;
34759                 s[i].style.left = (pos-this.splitOffset) + "px";
34760             }
34761         }
34762     },
34763
34764     handleHiddenChange : function(colModel, colIndex, hidden){
34765         if(hidden){
34766             this.hideColumn(colIndex);
34767         }else{
34768             this.unhideColumn(colIndex);
34769         }
34770     },
34771
34772     hideColumn : function(colIndex){
34773         var cid = this.getColumnId(colIndex);
34774         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
34775         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
34776         if(Roo.isSafari){
34777             this.updateHeaders();
34778         }
34779         this.updateSplitters();
34780         this.layout();
34781     },
34782
34783     unhideColumn : function(colIndex){
34784         var cid = this.getColumnId(colIndex);
34785         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
34786         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
34787
34788         if(Roo.isSafari){
34789             this.updateHeaders();
34790         }
34791         this.updateSplitters();
34792         this.layout();
34793     },
34794
34795     insertRows : function(dm, firstRow, lastRow, isUpdate){
34796         if(firstRow == 0 && lastRow == dm.getCount()-1){
34797             this.refresh();
34798         }else{
34799             if(!isUpdate){
34800                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
34801             }
34802             var s = this.getScrollState();
34803             var markup = this.renderRows(firstRow, lastRow);
34804             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
34805             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
34806             this.restoreScroll(s);
34807             if(!isUpdate){
34808                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
34809                 this.syncRowHeights(firstRow, lastRow);
34810                 this.stripeRows(firstRow);
34811                 this.layout();
34812             }
34813         }
34814     },
34815
34816     bufferRows : function(markup, target, index){
34817         var before = null, trows = target.rows, tbody = target.tBodies[0];
34818         if(index < trows.length){
34819             before = trows[index];
34820         }
34821         var b = document.createElement("div");
34822         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34823         var rows = b.firstChild.rows;
34824         for(var i = 0, len = rows.length; i < len; i++){
34825             if(before){
34826                 tbody.insertBefore(rows[0], before);
34827             }else{
34828                 tbody.appendChild(rows[0]);
34829             }
34830         }
34831         b.innerHTML = "";
34832         b = null;
34833     },
34834
34835     deleteRows : function(dm, firstRow, lastRow){
34836         if(dm.getRowCount()<1){
34837             this.fireEvent("beforerefresh", this);
34838             this.mainBody.update("");
34839             this.lockedBody.update("");
34840             this.fireEvent("refresh", this);
34841         }else{
34842             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34843             var bt = this.getBodyTable();
34844             var tbody = bt.firstChild;
34845             var rows = bt.rows;
34846             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34847                 tbody.removeChild(rows[firstRow]);
34848             }
34849             this.stripeRows(firstRow);
34850             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34851         }
34852     },
34853
34854     updateRows : function(dataSource, firstRow, lastRow){
34855         var s = this.getScrollState();
34856         this.refresh();
34857         this.restoreScroll(s);
34858     },
34859
34860     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34861         if(!noRefresh){
34862            this.refresh();
34863         }
34864         this.updateHeaderSortState();
34865     },
34866
34867     getScrollState : function(){
34868         
34869         var sb = this.scroller.dom;
34870         return {left: sb.scrollLeft, top: sb.scrollTop};
34871     },
34872
34873     stripeRows : function(startRow){
34874         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34875             return;
34876         }
34877         startRow = startRow || 0;
34878         var rows = this.getBodyTable().rows;
34879         var lrows = this.getLockedTable().rows;
34880         var cls = ' x-grid-row-alt ';
34881         for(var i = startRow, len = rows.length; i < len; i++){
34882             var row = rows[i], lrow = lrows[i];
34883             var isAlt = ((i+1) % 2 == 0);
34884             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34885             if(isAlt == hasAlt){
34886                 continue;
34887             }
34888             if(isAlt){
34889                 row.className += " x-grid-row-alt";
34890             }else{
34891                 row.className = row.className.replace("x-grid-row-alt", "");
34892             }
34893             if(lrow){
34894                 lrow.className = row.className;
34895             }
34896         }
34897     },
34898
34899     restoreScroll : function(state){
34900         //Roo.log('GridView.restoreScroll');
34901         var sb = this.scroller.dom;
34902         sb.scrollLeft = state.left;
34903         sb.scrollTop = state.top;
34904         this.syncScroll();
34905     },
34906
34907     syncScroll : function(){
34908         //Roo.log('GridView.syncScroll');
34909         var sb = this.scroller.dom;
34910         var sh = this.mainHd.dom;
34911         var bs = this.mainBody.dom;
34912         var lv = this.lockedBody.dom;
34913         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34914         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34915     },
34916
34917     handleScroll : function(e){
34918         this.syncScroll();
34919         var sb = this.scroller.dom;
34920         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34921         e.stopEvent();
34922     },
34923
34924     handleWheel : function(e){
34925         var d = e.getWheelDelta();
34926         this.scroller.dom.scrollTop -= d*22;
34927         // set this here to prevent jumpy scrolling on large tables
34928         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34929         e.stopEvent();
34930     },
34931
34932     renderRows : function(startRow, endRow){
34933         // pull in all the crap needed to render rows
34934         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34935         var colCount = cm.getColumnCount();
34936
34937         if(ds.getCount() < 1){
34938             return ["", ""];
34939         }
34940
34941         // build a map for all the columns
34942         var cs = [];
34943         for(var i = 0; i < colCount; i++){
34944             var name = cm.getDataIndex(i);
34945             cs[i] = {
34946                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34947                 renderer : cm.getRenderer(i),
34948                 id : cm.getColumnId(i),
34949                 locked : cm.isLocked(i)
34950             };
34951         }
34952
34953         startRow = startRow || 0;
34954         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34955
34956         // records to render
34957         var rs = ds.getRange(startRow, endRow);
34958
34959         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34960     },
34961
34962     // As much as I hate to duplicate code, this was branched because FireFox really hates
34963     // [].join("") on strings. The performance difference was substantial enough to
34964     // branch this function
34965     doRender : Roo.isGecko ?
34966             function(cs, rs, ds, startRow, colCount, stripe){
34967                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34968                 // buffers
34969                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34970                 
34971                 var hasListener = this.grid.hasListener('rowclass');
34972                 var rowcfg = {};
34973                 for(var j = 0, len = rs.length; j < len; j++){
34974                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34975                     for(var i = 0; i < colCount; i++){
34976                         c = cs[i];
34977                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34978                         p.id = c.id;
34979                         p.css = p.attr = "";
34980                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34981                         if(p.value == undefined || p.value === "") p.value = "&#160;";
34982                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34983                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
34984                         }
34985                         var markup = ct.apply(p);
34986                         if(!c.locked){
34987                             cb+= markup;
34988                         }else{
34989                             lcb+= markup;
34990                         }
34991                     }
34992                     var alt = [];
34993                     if(stripe && ((rowIndex+1) % 2 == 0)){
34994                         alt.push("x-grid-row-alt")
34995                     }
34996                     if(r.dirty){
34997                         alt.push(  " x-grid-dirty-row");
34998                     }
34999                     rp.cells = lcb;
35000                     if(this.getRowClass){
35001                         alt.push(this.getRowClass(r, rowIndex));
35002                     }
35003                     if (hasListener) {
35004                         rowcfg = {
35005                              
35006                             record: r,
35007                             rowIndex : rowIndex,
35008                             rowClass : ''
35009                         }
35010                         this.grid.fireEvent('rowclass', this, rowcfg);
35011                         alt.push(rowcfg.rowClass);
35012                     }
35013                     rp.alt = alt.join(" ");
35014                     lbuf+= rt.apply(rp);
35015                     rp.cells = cb;
35016                     buf+=  rt.apply(rp);
35017                 }
35018                 return [lbuf, buf];
35019             } :
35020             function(cs, rs, ds, startRow, colCount, stripe){
35021                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35022                 // buffers
35023                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35024                 var hasListener = this.grid.hasListener('rowclass');
35025  
35026                 var rowcfg = {};
35027                 for(var j = 0, len = rs.length; j < len; j++){
35028                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
35029                     for(var i = 0; i < colCount; i++){
35030                         c = cs[i];
35031                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35032                         p.id = c.id;
35033                         p.css = p.attr = "";
35034                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35035                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35036                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35037                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35038                         }
35039                         
35040                         var markup = ct.apply(p);
35041                         if(!c.locked){
35042                             cb[cb.length] = markup;
35043                         }else{
35044                             lcb[lcb.length] = markup;
35045                         }
35046                     }
35047                     var alt = [];
35048                     if(stripe && ((rowIndex+1) % 2 == 0)){
35049                         alt.push( "x-grid-row-alt");
35050                     }
35051                     if(r.dirty){
35052                         alt.push(" x-grid-dirty-row");
35053                     }
35054                     rp.cells = lcb;
35055                     if(this.getRowClass){
35056                         alt.push( this.getRowClass(r, rowIndex));
35057                     }
35058                     if (hasListener) {
35059                         rowcfg = {
35060                              
35061                             record: r,
35062                             rowIndex : rowIndex,
35063                             rowClass : ''
35064                         }
35065                         this.grid.fireEvent('rowclass', this, rowcfg);
35066                         alt.push(rowcfg.rowClass);
35067                     }
35068                     rp.alt = alt.join(" ");
35069                     rp.cells = lcb.join("");
35070                     lbuf[lbuf.length] = rt.apply(rp);
35071                     rp.cells = cb.join("");
35072                     buf[buf.length] =  rt.apply(rp);
35073                 }
35074                 return [lbuf.join(""), buf.join("")];
35075             },
35076
35077     renderBody : function(){
35078         var markup = this.renderRows();
35079         var bt = this.templates.body;
35080         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
35081     },
35082
35083     /**
35084      * Refreshes the grid
35085      * @param {Boolean} headersToo
35086      */
35087     refresh : function(headersToo){
35088         this.fireEvent("beforerefresh", this);
35089         this.grid.stopEditing();
35090         var result = this.renderBody();
35091         this.lockedBody.update(result[0]);
35092         this.mainBody.update(result[1]);
35093         if(headersToo === true){
35094             this.updateHeaders();
35095             this.updateColumns();
35096             this.updateSplitters();
35097             this.updateHeaderSortState();
35098         }
35099         this.syncRowHeights();
35100         this.layout();
35101         this.fireEvent("refresh", this);
35102     },
35103
35104     handleColumnMove : function(cm, oldIndex, newIndex){
35105         this.indexMap = null;
35106         var s = this.getScrollState();
35107         this.refresh(true);
35108         this.restoreScroll(s);
35109         this.afterMove(newIndex);
35110     },
35111
35112     afterMove : function(colIndex){
35113         if(this.enableMoveAnim && Roo.enableFx){
35114             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
35115         }
35116         // if multisort - fix sortOrder, and reload..
35117         if (this.grid.dataSource.multiSort) {
35118             // the we can call sort again..
35119             var dm = this.grid.dataSource;
35120             var cm = this.grid.colModel;
35121             var so = [];
35122             for(var i = 0; i < cm.config.length; i++ ) {
35123                 
35124                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
35125                     continue; // dont' bother, it's not in sort list or being set.
35126                 }
35127                 
35128                 so.push(cm.config[i].dataIndex);
35129             };
35130             dm.sortOrder = so;
35131             dm.load(dm.lastOptions);
35132             
35133             
35134         }
35135         
35136     },
35137
35138     updateCell : function(dm, rowIndex, dataIndex){
35139         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
35140         if(typeof colIndex == "undefined"){ // not present in grid
35141             return;
35142         }
35143         var cm = this.grid.colModel;
35144         var cell = this.getCell(rowIndex, colIndex);
35145         var cellText = this.getCellText(rowIndex, colIndex);
35146
35147         var p = {
35148             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
35149             id : cm.getColumnId(colIndex),
35150             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
35151         };
35152         var renderer = cm.getRenderer(colIndex);
35153         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
35154         if(typeof val == "undefined" || val === "") val = "&#160;";
35155         cellText.innerHTML = val;
35156         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
35157         this.syncRowHeights(rowIndex, rowIndex);
35158     },
35159
35160     calcColumnWidth : function(colIndex, maxRowsToMeasure){
35161         var maxWidth = 0;
35162         if(this.grid.autoSizeHeaders){
35163             var h = this.getHeaderCellMeasure(colIndex);
35164             maxWidth = Math.max(maxWidth, h.scrollWidth);
35165         }
35166         var tb, index;
35167         if(this.cm.isLocked(colIndex)){
35168             tb = this.getLockedTable();
35169             index = colIndex;
35170         }else{
35171             tb = this.getBodyTable();
35172             index = colIndex - this.cm.getLockedCount();
35173         }
35174         if(tb && tb.rows){
35175             var rows = tb.rows;
35176             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
35177             for(var i = 0; i < stopIndex; i++){
35178                 var cell = rows[i].childNodes[index].firstChild;
35179                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
35180             }
35181         }
35182         return maxWidth + /*margin for error in IE*/ 5;
35183     },
35184     /**
35185      * Autofit a column to its content.
35186      * @param {Number} colIndex
35187      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
35188      */
35189      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
35190          if(this.cm.isHidden(colIndex)){
35191              return; // can't calc a hidden column
35192          }
35193         if(forceMinSize){
35194             var cid = this.cm.getColumnId(colIndex);
35195             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
35196            if(this.grid.autoSizeHeaders){
35197                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
35198            }
35199         }
35200         var newWidth = this.calcColumnWidth(colIndex);
35201         this.cm.setColumnWidth(colIndex,
35202             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
35203         if(!suppressEvent){
35204             this.grid.fireEvent("columnresize", colIndex, newWidth);
35205         }
35206     },
35207
35208     /**
35209      * Autofits all columns to their content and then expands to fit any extra space in the grid
35210      */
35211      autoSizeColumns : function(){
35212         var cm = this.grid.colModel;
35213         var colCount = cm.getColumnCount();
35214         for(var i = 0; i < colCount; i++){
35215             this.autoSizeColumn(i, true, true);
35216         }
35217         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
35218             this.fitColumns();
35219         }else{
35220             this.updateColumns();
35221             this.layout();
35222         }
35223     },
35224
35225     /**
35226      * Autofits all columns to the grid's width proportionate with their current size
35227      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
35228      */
35229     fitColumns : function(reserveScrollSpace){
35230         var cm = this.grid.colModel;
35231         var colCount = cm.getColumnCount();
35232         var cols = [];
35233         var width = 0;
35234         var i, w;
35235         for (i = 0; i < colCount; i++){
35236             if(!cm.isHidden(i) && !cm.isFixed(i)){
35237                 w = cm.getColumnWidth(i);
35238                 cols.push(i);
35239                 cols.push(w);
35240                 width += w;
35241             }
35242         }
35243         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
35244         if(reserveScrollSpace){
35245             avail -= 17;
35246         }
35247         var frac = (avail - cm.getTotalWidth())/width;
35248         while (cols.length){
35249             w = cols.pop();
35250             i = cols.pop();
35251             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
35252         }
35253         this.updateColumns();
35254         this.layout();
35255     },
35256
35257     onRowSelect : function(rowIndex){
35258         var row = this.getRowComposite(rowIndex);
35259         row.addClass("x-grid-row-selected");
35260     },
35261
35262     onRowDeselect : function(rowIndex){
35263         var row = this.getRowComposite(rowIndex);
35264         row.removeClass("x-grid-row-selected");
35265     },
35266
35267     onCellSelect : function(row, col){
35268         var cell = this.getCell(row, col);
35269         if(cell){
35270             Roo.fly(cell).addClass("x-grid-cell-selected");
35271         }
35272     },
35273
35274     onCellDeselect : function(row, col){
35275         var cell = this.getCell(row, col);
35276         if(cell){
35277             Roo.fly(cell).removeClass("x-grid-cell-selected");
35278         }
35279     },
35280
35281     updateHeaderSortState : function(){
35282         
35283         // sort state can be single { field: xxx, direction : yyy}
35284         // or   { xxx=>ASC , yyy : DESC ..... }
35285         
35286         var mstate = {};
35287         if (!this.ds.multiSort) { 
35288             var state = this.ds.getSortState();
35289             if(!state){
35290                 return;
35291             }
35292             mstate[state.field] = state.direction;
35293             // FIXME... - this is not used here.. but might be elsewhere..
35294             this.sortState = state;
35295             
35296         } else {
35297             mstate = this.ds.sortToggle;
35298         }
35299         //remove existing sort classes..
35300         
35301         var sc = this.sortClasses;
35302         var hds = this.el.select(this.headerSelector).removeClass(sc);
35303         
35304         for(var f in mstate) {
35305         
35306             var sortColumn = this.cm.findColumnIndex(f);
35307             
35308             if(sortColumn != -1){
35309                 var sortDir = mstate[f];        
35310                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
35311             }
35312         }
35313         
35314          
35315         
35316     },
35317
35318
35319     handleHeaderClick : function(g, index){
35320         if(this.headersDisabled){
35321             return;
35322         }
35323         var dm = g.dataSource, cm = g.colModel;
35324         if(!cm.isSortable(index)){
35325             return;
35326         }
35327         g.stopEditing();
35328         
35329         if (dm.multiSort) {
35330             // update the sortOrder
35331             var so = [];
35332             for(var i = 0; i < cm.config.length; i++ ) {
35333                 
35334                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
35335                     continue; // dont' bother, it's not in sort list or being set.
35336                 }
35337                 
35338                 so.push(cm.config[i].dataIndex);
35339             };
35340             dm.sortOrder = so;
35341         }
35342         
35343         
35344         dm.sort(cm.getDataIndex(index));
35345     },
35346
35347
35348     destroy : function(){
35349         if(this.colMenu){
35350             this.colMenu.removeAll();
35351             Roo.menu.MenuMgr.unregister(this.colMenu);
35352             this.colMenu.getEl().remove();
35353             delete this.colMenu;
35354         }
35355         if(this.hmenu){
35356             this.hmenu.removeAll();
35357             Roo.menu.MenuMgr.unregister(this.hmenu);
35358             this.hmenu.getEl().remove();
35359             delete this.hmenu;
35360         }
35361         if(this.grid.enableColumnMove){
35362             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35363             if(dds){
35364                 for(var dd in dds){
35365                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
35366                         var elid = dds[dd].dragElId;
35367                         dds[dd].unreg();
35368                         Roo.get(elid).remove();
35369                     } else if(dds[dd].config.isTarget){
35370                         dds[dd].proxyTop.remove();
35371                         dds[dd].proxyBottom.remove();
35372                         dds[dd].unreg();
35373                     }
35374                     if(Roo.dd.DDM.locationCache[dd]){
35375                         delete Roo.dd.DDM.locationCache[dd];
35376                     }
35377                 }
35378                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35379             }
35380         }
35381         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
35382         this.bind(null, null);
35383         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
35384     },
35385
35386     handleLockChange : function(){
35387         this.refresh(true);
35388     },
35389
35390     onDenyColumnLock : function(){
35391
35392     },
35393
35394     onDenyColumnHide : function(){
35395
35396     },
35397
35398     handleHdMenuClick : function(item){
35399         var index = this.hdCtxIndex;
35400         var cm = this.cm, ds = this.ds;
35401         switch(item.id){
35402             case "asc":
35403                 ds.sort(cm.getDataIndex(index), "ASC");
35404                 break;
35405             case "desc":
35406                 ds.sort(cm.getDataIndex(index), "DESC");
35407                 break;
35408             case "lock":
35409                 var lc = cm.getLockedCount();
35410                 if(cm.getColumnCount(true) <= lc+1){
35411                     this.onDenyColumnLock();
35412                     return;
35413                 }
35414                 if(lc != index){
35415                     cm.setLocked(index, true, true);
35416                     cm.moveColumn(index, lc);
35417                     this.grid.fireEvent("columnmove", index, lc);
35418                 }else{
35419                     cm.setLocked(index, true);
35420                 }
35421             break;
35422             case "unlock":
35423                 var lc = cm.getLockedCount();
35424                 if((lc-1) != index){
35425                     cm.setLocked(index, false, true);
35426                     cm.moveColumn(index, lc-1);
35427                     this.grid.fireEvent("columnmove", index, lc-1);
35428                 }else{
35429                     cm.setLocked(index, false);
35430                 }
35431             break;
35432             default:
35433                 index = cm.getIndexById(item.id.substr(4));
35434                 if(index != -1){
35435                     if(item.checked && cm.getColumnCount(true) <= 1){
35436                         this.onDenyColumnHide();
35437                         return false;
35438                     }
35439                     cm.setHidden(index, item.checked);
35440                 }
35441         }
35442         return true;
35443     },
35444
35445     beforeColMenuShow : function(){
35446         var cm = this.cm,  colCount = cm.getColumnCount();
35447         this.colMenu.removeAll();
35448         for(var i = 0; i < colCount; i++){
35449             this.colMenu.add(new Roo.menu.CheckItem({
35450                 id: "col-"+cm.getColumnId(i),
35451                 text: cm.getColumnHeader(i),
35452                 checked: !cm.isHidden(i),
35453                 hideOnClick:false
35454             }));
35455         }
35456     },
35457
35458     handleHdCtx : function(g, index, e){
35459         e.stopEvent();
35460         var hd = this.getHeaderCell(index);
35461         this.hdCtxIndex = index;
35462         var ms = this.hmenu.items, cm = this.cm;
35463         ms.get("asc").setDisabled(!cm.isSortable(index));
35464         ms.get("desc").setDisabled(!cm.isSortable(index));
35465         if(this.grid.enableColLock !== false){
35466             ms.get("lock").setDisabled(cm.isLocked(index));
35467             ms.get("unlock").setDisabled(!cm.isLocked(index));
35468         }
35469         this.hmenu.show(hd, "tl-bl");
35470     },
35471
35472     handleHdOver : function(e){
35473         var hd = this.findHeaderCell(e.getTarget());
35474         if(hd && !this.headersDisabled){
35475             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
35476                this.fly(hd).addClass("x-grid-hd-over");
35477             }
35478         }
35479     },
35480
35481     handleHdOut : function(e){
35482         var hd = this.findHeaderCell(e.getTarget());
35483         if(hd){
35484             this.fly(hd).removeClass("x-grid-hd-over");
35485         }
35486     },
35487
35488     handleSplitDblClick : function(e, t){
35489         var i = this.getCellIndex(t);
35490         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
35491             this.autoSizeColumn(i, true);
35492             this.layout();
35493         }
35494     },
35495
35496     render : function(){
35497
35498         var cm = this.cm;
35499         var colCount = cm.getColumnCount();
35500
35501         if(this.grid.monitorWindowResize === true){
35502             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35503         }
35504         var header = this.renderHeaders();
35505         var body = this.templates.body.apply({rows:""});
35506         var html = this.templates.master.apply({
35507             lockedBody: body,
35508             body: body,
35509             lockedHeader: header[0],
35510             header: header[1]
35511         });
35512
35513         //this.updateColumns();
35514
35515         this.grid.getGridEl().dom.innerHTML = html;
35516
35517         this.initElements();
35518         
35519         // a kludge to fix the random scolling effect in webkit
35520         this.el.on("scroll", function() {
35521             this.el.dom.scrollTop=0; // hopefully not recursive..
35522         },this);
35523
35524         this.scroller.on("scroll", this.handleScroll, this);
35525         this.lockedBody.on("mousewheel", this.handleWheel, this);
35526         this.mainBody.on("mousewheel", this.handleWheel, this);
35527
35528         this.mainHd.on("mouseover", this.handleHdOver, this);
35529         this.mainHd.on("mouseout", this.handleHdOut, this);
35530         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
35531                 {delegate: "."+this.splitClass});
35532
35533         this.lockedHd.on("mouseover", this.handleHdOver, this);
35534         this.lockedHd.on("mouseout", this.handleHdOut, this);
35535         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
35536                 {delegate: "."+this.splitClass});
35537
35538         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
35539             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35540         }
35541
35542         this.updateSplitters();
35543
35544         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
35545             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35546             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35547         }
35548
35549         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
35550             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
35551             this.hmenu.add(
35552                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
35553                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
35554             );
35555             if(this.grid.enableColLock !== false){
35556                 this.hmenu.add('-',
35557                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
35558                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
35559                 );
35560             }
35561             if(this.grid.enableColumnHide !== false){
35562
35563                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
35564                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
35565                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
35566
35567                 this.hmenu.add('-',
35568                     {id:"columns", text: this.columnsText, menu: this.colMenu}
35569                 );
35570             }
35571             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
35572
35573             this.grid.on("headercontextmenu", this.handleHdCtx, this);
35574         }
35575
35576         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
35577             this.dd = new Roo.grid.GridDragZone(this.grid, {
35578                 ddGroup : this.grid.ddGroup || 'GridDD'
35579             });
35580         }
35581
35582         /*
35583         for(var i = 0; i < colCount; i++){
35584             if(cm.isHidden(i)){
35585                 this.hideColumn(i);
35586             }
35587             if(cm.config[i].align){
35588                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
35589                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
35590             }
35591         }*/
35592         
35593         this.updateHeaderSortState();
35594
35595         this.beforeInitialResize();
35596         this.layout(true);
35597
35598         // two part rendering gives faster view to the user
35599         this.renderPhase2.defer(1, this);
35600     },
35601
35602     renderPhase2 : function(){
35603         // render the rows now
35604         this.refresh();
35605         if(this.grid.autoSizeColumns){
35606             this.autoSizeColumns();
35607         }
35608     },
35609
35610     beforeInitialResize : function(){
35611
35612     },
35613
35614     onColumnSplitterMoved : function(i, w){
35615         this.userResized = true;
35616         var cm = this.grid.colModel;
35617         cm.setColumnWidth(i, w, true);
35618         var cid = cm.getColumnId(i);
35619         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35620         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35621         this.updateSplitters();
35622         this.layout();
35623         this.grid.fireEvent("columnresize", i, w);
35624     },
35625
35626     syncRowHeights : function(startIndex, endIndex){
35627         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
35628             startIndex = startIndex || 0;
35629             var mrows = this.getBodyTable().rows;
35630             var lrows = this.getLockedTable().rows;
35631             var len = mrows.length-1;
35632             endIndex = Math.min(endIndex || len, len);
35633             for(var i = startIndex; i <= endIndex; i++){
35634                 var m = mrows[i], l = lrows[i];
35635                 var h = Math.max(m.offsetHeight, l.offsetHeight);
35636                 m.style.height = l.style.height = h + "px";
35637             }
35638         }
35639     },
35640
35641     layout : function(initialRender, is2ndPass){
35642         var g = this.grid;
35643         var auto = g.autoHeight;
35644         var scrollOffset = 16;
35645         var c = g.getGridEl(), cm = this.cm,
35646                 expandCol = g.autoExpandColumn,
35647                 gv = this;
35648         //c.beginMeasure();
35649
35650         if(!c.dom.offsetWidth){ // display:none?
35651             if(initialRender){
35652                 this.lockedWrap.show();
35653                 this.mainWrap.show();
35654             }
35655             return;
35656         }
35657
35658         var hasLock = this.cm.isLocked(0);
35659
35660         var tbh = this.headerPanel.getHeight();
35661         var bbh = this.footerPanel.getHeight();
35662
35663         if(auto){
35664             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
35665             var newHeight = ch + c.getBorderWidth("tb");
35666             if(g.maxHeight){
35667                 newHeight = Math.min(g.maxHeight, newHeight);
35668             }
35669             c.setHeight(newHeight);
35670         }
35671
35672         if(g.autoWidth){
35673             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
35674         }
35675
35676         var s = this.scroller;
35677
35678         var csize = c.getSize(true);
35679
35680         this.el.setSize(csize.width, csize.height);
35681
35682         this.headerPanel.setWidth(csize.width);
35683         this.footerPanel.setWidth(csize.width);
35684
35685         var hdHeight = this.mainHd.getHeight();
35686         var vw = csize.width;
35687         var vh = csize.height - (tbh + bbh);
35688
35689         s.setSize(vw, vh);
35690
35691         var bt = this.getBodyTable();
35692         var ltWidth = hasLock ?
35693                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
35694
35695         var scrollHeight = bt.offsetHeight;
35696         var scrollWidth = ltWidth + bt.offsetWidth;
35697         var vscroll = false, hscroll = false;
35698
35699         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
35700
35701         var lw = this.lockedWrap, mw = this.mainWrap;
35702         var lb = this.lockedBody, mb = this.mainBody;
35703
35704         setTimeout(function(){
35705             var t = s.dom.offsetTop;
35706             var w = s.dom.clientWidth,
35707                 h = s.dom.clientHeight;
35708
35709             lw.setTop(t);
35710             lw.setSize(ltWidth, h);
35711
35712             mw.setLeftTop(ltWidth, t);
35713             mw.setSize(w-ltWidth, h);
35714
35715             lb.setHeight(h-hdHeight);
35716             mb.setHeight(h-hdHeight);
35717
35718             if(is2ndPass !== true && !gv.userResized && expandCol){
35719                 // high speed resize without full column calculation
35720                 
35721                 var ci = cm.getIndexById(expandCol);
35722                 if (ci < 0) {
35723                     ci = cm.findColumnIndex(expandCol);
35724                 }
35725                 ci = Math.max(0, ci); // make sure it's got at least the first col.
35726                 var expandId = cm.getColumnId(ci);
35727                 var  tw = cm.getTotalWidth(false);
35728                 var currentWidth = cm.getColumnWidth(ci);
35729                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
35730                 if(currentWidth != cw){
35731                     cm.setColumnWidth(ci, cw, true);
35732                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35733                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35734                     gv.updateSplitters();
35735                     gv.layout(false, true);
35736                 }
35737             }
35738
35739             if(initialRender){
35740                 lw.show();
35741                 mw.show();
35742             }
35743             //c.endMeasure();
35744         }, 10);
35745     },
35746
35747     onWindowResize : function(){
35748         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
35749             return;
35750         }
35751         this.layout();
35752     },
35753
35754     appendFooter : function(parentEl){
35755         return null;
35756     },
35757
35758     sortAscText : "Sort Ascending",
35759     sortDescText : "Sort Descending",
35760     lockText : "Lock Column",
35761     unlockText : "Unlock Column",
35762     columnsText : "Columns"
35763 });
35764
35765
35766 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35767     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35768     this.proxy.el.addClass('x-grid3-col-dd');
35769 };
35770
35771 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35772     handleMouseDown : function(e){
35773
35774     },
35775
35776     callHandleMouseDown : function(e){
35777         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35778     }
35779 });
35780 /*
35781  * Based on:
35782  * Ext JS Library 1.1.1
35783  * Copyright(c) 2006-2007, Ext JS, LLC.
35784  *
35785  * Originally Released Under LGPL - original licence link has changed is not relivant.
35786  *
35787  * Fork - LGPL
35788  * <script type="text/javascript">
35789  */
35790  
35791 // private
35792 // This is a support class used internally by the Grid components
35793 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35794     this.grid = grid;
35795     this.view = grid.getView();
35796     this.proxy = this.view.resizeProxy;
35797     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
35798         "gridSplitters" + this.grid.getGridEl().id, {
35799         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
35800     });
35801     this.setHandleElId(Roo.id(hd));
35802     this.setOuterHandleElId(Roo.id(hd2));
35803     this.scroll = false;
35804 };
35805 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35806     fly: Roo.Element.fly,
35807
35808     b4StartDrag : function(x, y){
35809         this.view.headersDisabled = true;
35810         this.proxy.setHeight(this.view.mainWrap.getHeight());
35811         var w = this.cm.getColumnWidth(this.cellIndex);
35812         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35813         this.resetConstraints();
35814         this.setXConstraint(minw, 1000);
35815         this.setYConstraint(0, 0);
35816         this.minX = x - minw;
35817         this.maxX = x + 1000;
35818         this.startPos = x;
35819         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35820     },
35821
35822
35823     handleMouseDown : function(e){
35824         ev = Roo.EventObject.setEvent(e);
35825         var t = this.fly(ev.getTarget());
35826         if(t.hasClass("x-grid-split")){
35827             this.cellIndex = this.view.getCellIndex(t.dom);
35828             this.split = t.dom;
35829             this.cm = this.grid.colModel;
35830             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35831                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35832             }
35833         }
35834     },
35835
35836     endDrag : function(e){
35837         this.view.headersDisabled = false;
35838         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35839         var diff = endX - this.startPos;
35840         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
35841     },
35842
35843     autoOffset : function(){
35844         this.setDelta(0,0);
35845     }
35846 });/*
35847  * Based on:
35848  * Ext JS Library 1.1.1
35849  * Copyright(c) 2006-2007, Ext JS, LLC.
35850  *
35851  * Originally Released Under LGPL - original licence link has changed is not relivant.
35852  *
35853  * Fork - LGPL
35854  * <script type="text/javascript">
35855  */
35856  
35857 // private
35858 // This is a support class used internally by the Grid components
35859 Roo.grid.GridDragZone = function(grid, config){
35860     this.view = grid.getView();
35861     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35862     if(this.view.lockedBody){
35863         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35864         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35865     }
35866     this.scroll = false;
35867     this.grid = grid;
35868     this.ddel = document.createElement('div');
35869     this.ddel.className = 'x-grid-dd-wrap';
35870 };
35871
35872 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35873     ddGroup : "GridDD",
35874
35875     getDragData : function(e){
35876         var t = Roo.lib.Event.getTarget(e);
35877         var rowIndex = this.view.findRowIndex(t);
35878         if(rowIndex !== false){
35879             var sm = this.grid.selModel;
35880             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35881               //  sm.mouseDown(e, t);
35882             //}
35883             if (e.hasModifier()){
35884                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35885             }
35886             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
35887         }
35888         return false;
35889     },
35890
35891     onInitDrag : function(e){
35892         var data = this.dragData;
35893         this.ddel.innerHTML = this.grid.getDragDropText();
35894         this.proxy.update(this.ddel);
35895         // fire start drag?
35896     },
35897
35898     afterRepair : function(){
35899         this.dragging = false;
35900     },
35901
35902     getRepairXY : function(e, data){
35903         return false;
35904     },
35905
35906     onEndDrag : function(data, e){
35907         // fire end drag?
35908     },
35909
35910     onValidDrop : function(dd, e, id){
35911         // fire drag drop?
35912         this.hideProxy();
35913     },
35914
35915     beforeInvalidDrop : function(e, id){
35916
35917     }
35918 });/*
35919  * Based on:
35920  * Ext JS Library 1.1.1
35921  * Copyright(c) 2006-2007, Ext JS, LLC.
35922  *
35923  * Originally Released Under LGPL - original licence link has changed is not relivant.
35924  *
35925  * Fork - LGPL
35926  * <script type="text/javascript">
35927  */
35928  
35929
35930 /**
35931  * @class Roo.grid.ColumnModel
35932  * @extends Roo.util.Observable
35933  * This is the default implementation of a ColumnModel used by the Grid. It defines
35934  * the columns in the grid.
35935  * <br>Usage:<br>
35936  <pre><code>
35937  var colModel = new Roo.grid.ColumnModel([
35938         {header: "Ticker", width: 60, sortable: true, locked: true},
35939         {header: "Company Name", width: 150, sortable: true},
35940         {header: "Market Cap.", width: 100, sortable: true},
35941         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35942         {header: "Employees", width: 100, sortable: true, resizable: false}
35943  ]);
35944  </code></pre>
35945  * <p>
35946  
35947  * The config options listed for this class are options which may appear in each
35948  * individual column definition.
35949  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35950  * @constructor
35951  * @param {Object} config An Array of column config objects. See this class's
35952  * config objects for details.
35953 */
35954 Roo.grid.ColumnModel = function(config){
35955         /**
35956      * The config passed into the constructor
35957      */
35958     this.config = config;
35959     this.lookup = {};
35960
35961     // if no id, create one
35962     // if the column does not have a dataIndex mapping,
35963     // map it to the order it is in the config
35964     for(var i = 0, len = config.length; i < len; i++){
35965         var c = config[i];
35966         if(typeof c.dataIndex == "undefined"){
35967             c.dataIndex = i;
35968         }
35969         if(typeof c.renderer == "string"){
35970             c.renderer = Roo.util.Format[c.renderer];
35971         }
35972         if(typeof c.id == "undefined"){
35973             c.id = Roo.id();
35974         }
35975         if(c.editor && c.editor.xtype){
35976             c.editor  = Roo.factory(c.editor, Roo.grid);
35977         }
35978         if(c.editor && c.editor.isFormField){
35979             c.editor = new Roo.grid.GridEditor(c.editor);
35980         }
35981         this.lookup[c.id] = c;
35982     }
35983
35984     /**
35985      * The width of columns which have no width specified (defaults to 100)
35986      * @type Number
35987      */
35988     this.defaultWidth = 100;
35989
35990     /**
35991      * Default sortable of columns which have no sortable specified (defaults to false)
35992      * @type Boolean
35993      */
35994     this.defaultSortable = false;
35995
35996     this.addEvents({
35997         /**
35998              * @event widthchange
35999              * Fires when the width of a column changes.
36000              * @param {ColumnModel} this
36001              * @param {Number} columnIndex The column index
36002              * @param {Number} newWidth The new width
36003              */
36004             "widthchange": true,
36005         /**
36006              * @event headerchange
36007              * Fires when the text of a header changes.
36008              * @param {ColumnModel} this
36009              * @param {Number} columnIndex The column index
36010              * @param {Number} newText The new header text
36011              */
36012             "headerchange": true,
36013         /**
36014              * @event hiddenchange
36015              * Fires when a column is hidden or "unhidden".
36016              * @param {ColumnModel} this
36017              * @param {Number} columnIndex The column index
36018              * @param {Boolean} hidden true if hidden, false otherwise
36019              */
36020             "hiddenchange": true,
36021             /**
36022          * @event columnmoved
36023          * Fires when a column is moved.
36024          * @param {ColumnModel} this
36025          * @param {Number} oldIndex
36026          * @param {Number} newIndex
36027          */
36028         "columnmoved" : true,
36029         /**
36030          * @event columlockchange
36031          * Fires when a column's locked state is changed
36032          * @param {ColumnModel} this
36033          * @param {Number} colIndex
36034          * @param {Boolean} locked true if locked
36035          */
36036         "columnlockchange" : true
36037     });
36038     Roo.grid.ColumnModel.superclass.constructor.call(this);
36039 };
36040 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
36041     /**
36042      * @cfg {String} header The header text to display in the Grid view.
36043      */
36044     /**
36045      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
36046      * {@link Roo.data.Record} definition from which to draw the column's value. If not
36047      * specified, the column's index is used as an index into the Record's data Array.
36048      */
36049     /**
36050      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
36051      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
36052      */
36053     /**
36054      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
36055      * Defaults to the value of the {@link #defaultSortable} property.
36056      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
36057      */
36058     /**
36059      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
36060      */
36061     /**
36062      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
36063      */
36064     /**
36065      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
36066      */
36067     /**
36068      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
36069      */
36070     /**
36071      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
36072      * given the cell's data value. See {@link #setRenderer}. If not specified, the
36073      * default renderer uses the raw data value.
36074      */
36075        /**
36076      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
36077      */
36078     /**
36079      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
36080      */
36081
36082     /**
36083      * Returns the id of the column at the specified index.
36084      * @param {Number} index The column index
36085      * @return {String} the id
36086      */
36087     getColumnId : function(index){
36088         return this.config[index].id;
36089     },
36090
36091     /**
36092      * Returns the column for a specified id.
36093      * @param {String} id The column id
36094      * @return {Object} the column
36095      */
36096     getColumnById : function(id){
36097         return this.lookup[id];
36098     },
36099
36100     
36101     /**
36102      * Returns the column for a specified dataIndex.
36103      * @param {String} dataIndex The column dataIndex
36104      * @return {Object|Boolean} the column or false if not found
36105      */
36106     getColumnByDataIndex: function(dataIndex){
36107         var index = this.findColumnIndex(dataIndex);
36108         return index > -1 ? this.config[index] : false;
36109     },
36110     
36111     /**
36112      * Returns the index for a specified column id.
36113      * @param {String} id The column id
36114      * @return {Number} the index, or -1 if not found
36115      */
36116     getIndexById : function(id){
36117         for(var i = 0, len = this.config.length; i < len; i++){
36118             if(this.config[i].id == id){
36119                 return i;
36120             }
36121         }
36122         return -1;
36123     },
36124     
36125     /**
36126      * Returns the index for a specified column dataIndex.
36127      * @param {String} dataIndex The column dataIndex
36128      * @return {Number} the index, or -1 if not found
36129      */
36130     
36131     findColumnIndex : function(dataIndex){
36132         for(var i = 0, len = this.config.length; i < len; i++){
36133             if(this.config[i].dataIndex == dataIndex){
36134                 return i;
36135             }
36136         }
36137         return -1;
36138     },
36139     
36140     
36141     moveColumn : function(oldIndex, newIndex){
36142         var c = this.config[oldIndex];
36143         this.config.splice(oldIndex, 1);
36144         this.config.splice(newIndex, 0, c);
36145         this.dataMap = null;
36146         this.fireEvent("columnmoved", this, oldIndex, newIndex);
36147     },
36148
36149     isLocked : function(colIndex){
36150         return this.config[colIndex].locked === true;
36151     },
36152
36153     setLocked : function(colIndex, value, suppressEvent){
36154         if(this.isLocked(colIndex) == value){
36155             return;
36156         }
36157         this.config[colIndex].locked = value;
36158         if(!suppressEvent){
36159             this.fireEvent("columnlockchange", this, colIndex, value);
36160         }
36161     },
36162
36163     getTotalLockedWidth : function(){
36164         var totalWidth = 0;
36165         for(var i = 0; i < this.config.length; i++){
36166             if(this.isLocked(i) && !this.isHidden(i)){
36167                 this.totalWidth += this.getColumnWidth(i);
36168             }
36169         }
36170         return totalWidth;
36171     },
36172
36173     getLockedCount : function(){
36174         for(var i = 0, len = this.config.length; i < len; i++){
36175             if(!this.isLocked(i)){
36176                 return i;
36177             }
36178         }
36179     },
36180
36181     /**
36182      * Returns the number of columns.
36183      * @return {Number}
36184      */
36185     getColumnCount : function(visibleOnly){
36186         if(visibleOnly === true){
36187             var c = 0;
36188             for(var i = 0, len = this.config.length; i < len; i++){
36189                 if(!this.isHidden(i)){
36190                     c++;
36191                 }
36192             }
36193             return c;
36194         }
36195         return this.config.length;
36196     },
36197
36198     /**
36199      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
36200      * @param {Function} fn
36201      * @param {Object} scope (optional)
36202      * @return {Array} result
36203      */
36204     getColumnsBy : function(fn, scope){
36205         var r = [];
36206         for(var i = 0, len = this.config.length; i < len; i++){
36207             var c = this.config[i];
36208             if(fn.call(scope||this, c, i) === true){
36209                 r[r.length] = c;
36210             }
36211         }
36212         return r;
36213     },
36214
36215     /**
36216      * Returns true if the specified column is sortable.
36217      * @param {Number} col The column index
36218      * @return {Boolean}
36219      */
36220     isSortable : function(col){
36221         if(typeof this.config[col].sortable == "undefined"){
36222             return this.defaultSortable;
36223         }
36224         return this.config[col].sortable;
36225     },
36226
36227     /**
36228      * Returns the rendering (formatting) function defined for the column.
36229      * @param {Number} col The column index.
36230      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
36231      */
36232     getRenderer : function(col){
36233         if(!this.config[col].renderer){
36234             return Roo.grid.ColumnModel.defaultRenderer;
36235         }
36236         return this.config[col].renderer;
36237     },
36238
36239     /**
36240      * Sets the rendering (formatting) function for a column.
36241      * @param {Number} col The column index
36242      * @param {Function} fn The function to use to process the cell's raw data
36243      * to return HTML markup for the grid view. The render function is called with
36244      * the following parameters:<ul>
36245      * <li>Data value.</li>
36246      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
36247      * <li>css A CSS style string to apply to the table cell.</li>
36248      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
36249      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
36250      * <li>Row index</li>
36251      * <li>Column index</li>
36252      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
36253      */
36254     setRenderer : function(col, fn){
36255         this.config[col].renderer = fn;
36256     },
36257
36258     /**
36259      * Returns the width for the specified column.
36260      * @param {Number} col The column index
36261      * @return {Number}
36262      */
36263     getColumnWidth : function(col){
36264         return this.config[col].width * 1 || this.defaultWidth;
36265     },
36266
36267     /**
36268      * Sets the width for a column.
36269      * @param {Number} col The column index
36270      * @param {Number} width The new width
36271      */
36272     setColumnWidth : function(col, width, suppressEvent){
36273         this.config[col].width = width;
36274         this.totalWidth = null;
36275         if(!suppressEvent){
36276              this.fireEvent("widthchange", this, col, width);
36277         }
36278     },
36279
36280     /**
36281      * Returns the total width of all columns.
36282      * @param {Boolean} includeHidden True to include hidden column widths
36283      * @return {Number}
36284      */
36285     getTotalWidth : function(includeHidden){
36286         if(!this.totalWidth){
36287             this.totalWidth = 0;
36288             for(var i = 0, len = this.config.length; i < len; i++){
36289                 if(includeHidden || !this.isHidden(i)){
36290                     this.totalWidth += this.getColumnWidth(i);
36291                 }
36292             }
36293         }
36294         return this.totalWidth;
36295     },
36296
36297     /**
36298      * Returns the header for the specified column.
36299      * @param {Number} col The column index
36300      * @return {String}
36301      */
36302     getColumnHeader : function(col){
36303         return this.config[col].header;
36304     },
36305
36306     /**
36307      * Sets the header for a column.
36308      * @param {Number} col The column index
36309      * @param {String} header The new header
36310      */
36311     setColumnHeader : function(col, header){
36312         this.config[col].header = header;
36313         this.fireEvent("headerchange", this, col, header);
36314     },
36315
36316     /**
36317      * Returns the tooltip for the specified column.
36318      * @param {Number} col The column index
36319      * @return {String}
36320      */
36321     getColumnTooltip : function(col){
36322             return this.config[col].tooltip;
36323     },
36324     /**
36325      * Sets the tooltip for a column.
36326      * @param {Number} col The column index
36327      * @param {String} tooltip The new tooltip
36328      */
36329     setColumnTooltip : function(col, tooltip){
36330             this.config[col].tooltip = tooltip;
36331     },
36332
36333     /**
36334      * Returns the dataIndex for the specified column.
36335      * @param {Number} col The column index
36336      * @return {Number}
36337      */
36338     getDataIndex : function(col){
36339         return this.config[col].dataIndex;
36340     },
36341
36342     /**
36343      * Sets the dataIndex for a column.
36344      * @param {Number} col The column index
36345      * @param {Number} dataIndex The new dataIndex
36346      */
36347     setDataIndex : function(col, dataIndex){
36348         this.config[col].dataIndex = dataIndex;
36349     },
36350
36351     
36352     
36353     /**
36354      * Returns true if the cell is editable.
36355      * @param {Number} colIndex The column index
36356      * @param {Number} rowIndex The row index
36357      * @return {Boolean}
36358      */
36359     isCellEditable : function(colIndex, rowIndex){
36360         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
36361     },
36362
36363     /**
36364      * Returns the editor defined for the cell/column.
36365      * return false or null to disable editing.
36366      * @param {Number} colIndex The column index
36367      * @param {Number} rowIndex The row index
36368      * @return {Object}
36369      */
36370     getCellEditor : function(colIndex, rowIndex){
36371         return this.config[colIndex].editor;
36372     },
36373
36374     /**
36375      * Sets if a column is editable.
36376      * @param {Number} col The column index
36377      * @param {Boolean} editable True if the column is editable
36378      */
36379     setEditable : function(col, editable){
36380         this.config[col].editable = editable;
36381     },
36382
36383
36384     /**
36385      * Returns true if the column is hidden.
36386      * @param {Number} colIndex The column index
36387      * @return {Boolean}
36388      */
36389     isHidden : function(colIndex){
36390         return this.config[colIndex].hidden;
36391     },
36392
36393
36394     /**
36395      * Returns true if the column width cannot be changed
36396      */
36397     isFixed : function(colIndex){
36398         return this.config[colIndex].fixed;
36399     },
36400
36401     /**
36402      * Returns true if the column can be resized
36403      * @return {Boolean}
36404      */
36405     isResizable : function(colIndex){
36406         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
36407     },
36408     /**
36409      * Sets if a column is hidden.
36410      * @param {Number} colIndex The column index
36411      * @param {Boolean} hidden True if the column is hidden
36412      */
36413     setHidden : function(colIndex, hidden){
36414         this.config[colIndex].hidden = hidden;
36415         this.totalWidth = null;
36416         this.fireEvent("hiddenchange", this, colIndex, hidden);
36417     },
36418
36419     /**
36420      * Sets the editor for a column.
36421      * @param {Number} col The column index
36422      * @param {Object} editor The editor object
36423      */
36424     setEditor : function(col, editor){
36425         this.config[col].editor = editor;
36426     }
36427 });
36428
36429 Roo.grid.ColumnModel.defaultRenderer = function(value){
36430         if(typeof value == "string" && value.length < 1){
36431             return "&#160;";
36432         }
36433         return value;
36434 };
36435
36436 // Alias for backwards compatibility
36437 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
36438 /*
36439  * Based on:
36440  * Ext JS Library 1.1.1
36441  * Copyright(c) 2006-2007, Ext JS, LLC.
36442  *
36443  * Originally Released Under LGPL - original licence link has changed is not relivant.
36444  *
36445  * Fork - LGPL
36446  * <script type="text/javascript">
36447  */
36448
36449 /**
36450  * @class Roo.grid.AbstractSelectionModel
36451  * @extends Roo.util.Observable
36452  * Abstract base class for grid SelectionModels.  It provides the interface that should be
36453  * implemented by descendant classes.  This class should not be directly instantiated.
36454  * @constructor
36455  */
36456 Roo.grid.AbstractSelectionModel = function(){
36457     this.locked = false;
36458     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
36459 };
36460
36461 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
36462     /** @ignore Called by the grid automatically. Do not call directly. */
36463     init : function(grid){
36464         this.grid = grid;
36465         this.initEvents();
36466     },
36467
36468     /**
36469      * Locks the selections.
36470      */
36471     lock : function(){
36472         this.locked = true;
36473     },
36474
36475     /**
36476      * Unlocks the selections.
36477      */
36478     unlock : function(){
36479         this.locked = false;
36480     },
36481
36482     /**
36483      * Returns true if the selections are locked.
36484      * @return {Boolean}
36485      */
36486     isLocked : function(){
36487         return this.locked;
36488     }
36489 });/*
36490  * Based on:
36491  * Ext JS Library 1.1.1
36492  * Copyright(c) 2006-2007, Ext JS, LLC.
36493  *
36494  * Originally Released Under LGPL - original licence link has changed is not relivant.
36495  *
36496  * Fork - LGPL
36497  * <script type="text/javascript">
36498  */
36499 /**
36500  * @extends Roo.grid.AbstractSelectionModel
36501  * @class Roo.grid.RowSelectionModel
36502  * The default SelectionModel used by {@link Roo.grid.Grid}.
36503  * It supports multiple selections and keyboard selection/navigation. 
36504  * @constructor
36505  * @param {Object} config
36506  */
36507 Roo.grid.RowSelectionModel = function(config){
36508     Roo.apply(this, config);
36509     this.selections = new Roo.util.MixedCollection(false, function(o){
36510         return o.id;
36511     });
36512
36513     this.last = false;
36514     this.lastActive = false;
36515
36516     this.addEvents({
36517         /**
36518              * @event selectionchange
36519              * Fires when the selection changes
36520              * @param {SelectionModel} this
36521              */
36522             "selectionchange" : true,
36523         /**
36524              * @event afterselectionchange
36525              * Fires after the selection changes (eg. by key press or clicking)
36526              * @param {SelectionModel} this
36527              */
36528             "afterselectionchange" : true,
36529         /**
36530              * @event beforerowselect
36531              * Fires when a row is selected being selected, return false to cancel.
36532              * @param {SelectionModel} this
36533              * @param {Number} rowIndex The selected index
36534              * @param {Boolean} keepExisting False if other selections will be cleared
36535              */
36536             "beforerowselect" : true,
36537         /**
36538              * @event rowselect
36539              * Fires when a row is selected.
36540              * @param {SelectionModel} this
36541              * @param {Number} rowIndex The selected index
36542              * @param {Roo.data.Record} r The record
36543              */
36544             "rowselect" : true,
36545         /**
36546              * @event rowdeselect
36547              * Fires when a row is deselected.
36548              * @param {SelectionModel} this
36549              * @param {Number} rowIndex The selected index
36550              */
36551         "rowdeselect" : true
36552     });
36553     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36554     this.locked = false;
36555 };
36556
36557 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
36558     /**
36559      * @cfg {Boolean} singleSelect
36560      * True to allow selection of only one row at a time (defaults to false)
36561      */
36562     singleSelect : false,
36563
36564     // private
36565     initEvents : function(){
36566
36567         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36568             this.grid.on("mousedown", this.handleMouseDown, this);
36569         }else{ // allow click to work like normal
36570             this.grid.on("rowclick", this.handleDragableRowClick, this);
36571         }
36572
36573         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36574             "up" : function(e){
36575                 if(!e.shiftKey){
36576                     this.selectPrevious(e.shiftKey);
36577                 }else if(this.last !== false && this.lastActive !== false){
36578                     var last = this.last;
36579                     this.selectRange(this.last,  this.lastActive-1);
36580                     this.grid.getView().focusRow(this.lastActive);
36581                     if(last !== false){
36582                         this.last = last;
36583                     }
36584                 }else{
36585                     this.selectFirstRow();
36586                 }
36587                 this.fireEvent("afterselectionchange", this);
36588             },
36589             "down" : function(e){
36590                 if(!e.shiftKey){
36591                     this.selectNext(e.shiftKey);
36592                 }else if(this.last !== false && this.lastActive !== false){
36593                     var last = this.last;
36594                     this.selectRange(this.last,  this.lastActive+1);
36595                     this.grid.getView().focusRow(this.lastActive);
36596                     if(last !== false){
36597                         this.last = last;
36598                     }
36599                 }else{
36600                     this.selectFirstRow();
36601                 }
36602                 this.fireEvent("afterselectionchange", this);
36603             },
36604             scope: this
36605         });
36606
36607         var view = this.grid.view;
36608         view.on("refresh", this.onRefresh, this);
36609         view.on("rowupdated", this.onRowUpdated, this);
36610         view.on("rowremoved", this.onRemove, this);
36611     },
36612
36613     // private
36614     onRefresh : function(){
36615         var ds = this.grid.dataSource, i, v = this.grid.view;
36616         var s = this.selections;
36617         s.each(function(r){
36618             if((i = ds.indexOfId(r.id)) != -1){
36619                 v.onRowSelect(i);
36620             }else{
36621                 s.remove(r);
36622             }
36623         });
36624     },
36625
36626     // private
36627     onRemove : function(v, index, r){
36628         this.selections.remove(r);
36629     },
36630
36631     // private
36632     onRowUpdated : function(v, index, r){
36633         if(this.isSelected(r)){
36634             v.onRowSelect(index);
36635         }
36636     },
36637
36638     /**
36639      * Select records.
36640      * @param {Array} records The records to select
36641      * @param {Boolean} keepExisting (optional) True to keep existing selections
36642      */
36643     selectRecords : function(records, keepExisting){
36644         if(!keepExisting){
36645             this.clearSelections();
36646         }
36647         var ds = this.grid.dataSource;
36648         for(var i = 0, len = records.length; i < len; i++){
36649             this.selectRow(ds.indexOf(records[i]), true);
36650         }
36651     },
36652
36653     /**
36654      * Gets the number of selected rows.
36655      * @return {Number}
36656      */
36657     getCount : function(){
36658         return this.selections.length;
36659     },
36660
36661     /**
36662      * Selects the first row in the grid.
36663      */
36664     selectFirstRow : function(){
36665         this.selectRow(0);
36666     },
36667
36668     /**
36669      * Select the last row.
36670      * @param {Boolean} keepExisting (optional) True to keep existing selections
36671      */
36672     selectLastRow : function(keepExisting){
36673         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
36674     },
36675
36676     /**
36677      * Selects the row immediately following the last selected row.
36678      * @param {Boolean} keepExisting (optional) True to keep existing selections
36679      */
36680     selectNext : function(keepExisting){
36681         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
36682             this.selectRow(this.last+1, keepExisting);
36683             this.grid.getView().focusRow(this.last);
36684         }
36685     },
36686
36687     /**
36688      * Selects the row that precedes the last selected row.
36689      * @param {Boolean} keepExisting (optional) True to keep existing selections
36690      */
36691     selectPrevious : function(keepExisting){
36692         if(this.last){
36693             this.selectRow(this.last-1, keepExisting);
36694             this.grid.getView().focusRow(this.last);
36695         }
36696     },
36697
36698     /**
36699      * Returns the selected records
36700      * @return {Array} Array of selected records
36701      */
36702     getSelections : function(){
36703         return [].concat(this.selections.items);
36704     },
36705
36706     /**
36707      * Returns the first selected record.
36708      * @return {Record}
36709      */
36710     getSelected : function(){
36711         return this.selections.itemAt(0);
36712     },
36713
36714
36715     /**
36716      * Clears all selections.
36717      */
36718     clearSelections : function(fast){
36719         if(this.locked) return;
36720         if(fast !== true){
36721             var ds = this.grid.dataSource;
36722             var s = this.selections;
36723             s.each(function(r){
36724                 this.deselectRow(ds.indexOfId(r.id));
36725             }, this);
36726             s.clear();
36727         }else{
36728             this.selections.clear();
36729         }
36730         this.last = false;
36731     },
36732
36733
36734     /**
36735      * Selects all rows.
36736      */
36737     selectAll : function(){
36738         if(this.locked) return;
36739         this.selections.clear();
36740         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
36741             this.selectRow(i, true);
36742         }
36743     },
36744
36745     /**
36746      * Returns True if there is a selection.
36747      * @return {Boolean}
36748      */
36749     hasSelection : function(){
36750         return this.selections.length > 0;
36751     },
36752
36753     /**
36754      * Returns True if the specified row is selected.
36755      * @param {Number/Record} record The record or index of the record to check
36756      * @return {Boolean}
36757      */
36758     isSelected : function(index){
36759         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
36760         return (r && this.selections.key(r.id) ? true : false);
36761     },
36762
36763     /**
36764      * Returns True if the specified record id is selected.
36765      * @param {String} id The id of record to check
36766      * @return {Boolean}
36767      */
36768     isIdSelected : function(id){
36769         return (this.selections.key(id) ? true : false);
36770     },
36771
36772     // private
36773     handleMouseDown : function(e, t){
36774         var view = this.grid.getView(), rowIndex;
36775         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36776             return;
36777         };
36778         if(e.shiftKey && this.last !== false){
36779             var last = this.last;
36780             this.selectRange(last, rowIndex, e.ctrlKey);
36781             this.last = last; // reset the last
36782             view.focusRow(rowIndex);
36783         }else{
36784             var isSelected = this.isSelected(rowIndex);
36785             if(e.button !== 0 && isSelected){
36786                 view.focusRow(rowIndex);
36787             }else if(e.ctrlKey && isSelected){
36788                 this.deselectRow(rowIndex);
36789             }else if(!isSelected){
36790                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36791                 view.focusRow(rowIndex);
36792             }
36793         }
36794         this.fireEvent("afterselectionchange", this);
36795     },
36796     // private
36797     handleDragableRowClick :  function(grid, rowIndex, e) 
36798     {
36799         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36800             this.selectRow(rowIndex, false);
36801             grid.view.focusRow(rowIndex);
36802              this.fireEvent("afterselectionchange", this);
36803         }
36804     },
36805     
36806     /**
36807      * Selects multiple rows.
36808      * @param {Array} rows Array of the indexes of the row to select
36809      * @param {Boolean} keepExisting (optional) True to keep existing selections
36810      */
36811     selectRows : function(rows, keepExisting){
36812         if(!keepExisting){
36813             this.clearSelections();
36814         }
36815         for(var i = 0, len = rows.length; i < len; i++){
36816             this.selectRow(rows[i], true);
36817         }
36818     },
36819
36820     /**
36821      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36822      * @param {Number} startRow The index of the first row in the range
36823      * @param {Number} endRow The index of the last row in the range
36824      * @param {Boolean} keepExisting (optional) True to retain existing selections
36825      */
36826     selectRange : function(startRow, endRow, keepExisting){
36827         if(this.locked) return;
36828         if(!keepExisting){
36829             this.clearSelections();
36830         }
36831         if(startRow <= endRow){
36832             for(var i = startRow; i <= endRow; i++){
36833                 this.selectRow(i, true);
36834             }
36835         }else{
36836             for(var i = startRow; i >= endRow; i--){
36837                 this.selectRow(i, true);
36838             }
36839         }
36840     },
36841
36842     /**
36843      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36844      * @param {Number} startRow The index of the first row in the range
36845      * @param {Number} endRow The index of the last row in the range
36846      */
36847     deselectRange : function(startRow, endRow, preventViewNotify){
36848         if(this.locked) return;
36849         for(var i = startRow; i <= endRow; i++){
36850             this.deselectRow(i, preventViewNotify);
36851         }
36852     },
36853
36854     /**
36855      * Selects a row.
36856      * @param {Number} row The index of the row to select
36857      * @param {Boolean} keepExisting (optional) True to keep existing selections
36858      */
36859     selectRow : function(index, keepExisting, preventViewNotify){
36860         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
36861         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36862             if(!keepExisting || this.singleSelect){
36863                 this.clearSelections();
36864             }
36865             var r = this.grid.dataSource.getAt(index);
36866             this.selections.add(r);
36867             this.last = this.lastActive = index;
36868             if(!preventViewNotify){
36869                 this.grid.getView().onRowSelect(index);
36870             }
36871             this.fireEvent("rowselect", this, index, r);
36872             this.fireEvent("selectionchange", this);
36873         }
36874     },
36875
36876     /**
36877      * Deselects a row.
36878      * @param {Number} row The index of the row to deselect
36879      */
36880     deselectRow : function(index, preventViewNotify){
36881         if(this.locked) return;
36882         if(this.last == index){
36883             this.last = false;
36884         }
36885         if(this.lastActive == index){
36886             this.lastActive = false;
36887         }
36888         var r = this.grid.dataSource.getAt(index);
36889         this.selections.remove(r);
36890         if(!preventViewNotify){
36891             this.grid.getView().onRowDeselect(index);
36892         }
36893         this.fireEvent("rowdeselect", this, index);
36894         this.fireEvent("selectionchange", this);
36895     },
36896
36897     // private
36898     restoreLast : function(){
36899         if(this._last){
36900             this.last = this._last;
36901         }
36902     },
36903
36904     // private
36905     acceptsNav : function(row, col, cm){
36906         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36907     },
36908
36909     // private
36910     onEditorKey : function(field, e){
36911         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36912         if(k == e.TAB){
36913             e.stopEvent();
36914             ed.completeEdit();
36915             if(e.shiftKey){
36916                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36917             }else{
36918                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36919             }
36920         }else if(k == e.ENTER && !e.ctrlKey){
36921             e.stopEvent();
36922             ed.completeEdit();
36923             if(e.shiftKey){
36924                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36925             }else{
36926                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36927             }
36928         }else if(k == e.ESC){
36929             ed.cancelEdit();
36930         }
36931         if(newCell){
36932             g.startEditing(newCell[0], newCell[1]);
36933         }
36934     }
36935 });/*
36936  * Based on:
36937  * Ext JS Library 1.1.1
36938  * Copyright(c) 2006-2007, Ext JS, LLC.
36939  *
36940  * Originally Released Under LGPL - original licence link has changed is not relivant.
36941  *
36942  * Fork - LGPL
36943  * <script type="text/javascript">
36944  */
36945 /**
36946  * @class Roo.grid.CellSelectionModel
36947  * @extends Roo.grid.AbstractSelectionModel
36948  * This class provides the basic implementation for cell selection in a grid.
36949  * @constructor
36950  * @param {Object} config The object containing the configuration of this model.
36951  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36952  */
36953 Roo.grid.CellSelectionModel = function(config){
36954     Roo.apply(this, config);
36955
36956     this.selection = null;
36957
36958     this.addEvents({
36959         /**
36960              * @event beforerowselect
36961              * Fires before a cell is selected.
36962              * @param {SelectionModel} this
36963              * @param {Number} rowIndex The selected row index
36964              * @param {Number} colIndex The selected cell index
36965              */
36966             "beforecellselect" : true,
36967         /**
36968              * @event cellselect
36969              * Fires when a cell is selected.
36970              * @param {SelectionModel} this
36971              * @param {Number} rowIndex The selected row index
36972              * @param {Number} colIndex The selected cell index
36973              */
36974             "cellselect" : true,
36975         /**
36976              * @event selectionchange
36977              * Fires when the active selection changes.
36978              * @param {SelectionModel} this
36979              * @param {Object} selection null for no selection or an object (o) with two properties
36980                 <ul>
36981                 <li>o.record: the record object for the row the selection is in</li>
36982                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36983                 </ul>
36984              */
36985             "selectionchange" : true,
36986         /**
36987              * @event tabend
36988              * Fires when the tab (or enter) was pressed on the last editable cell
36989              * You can use this to trigger add new row.
36990              * @param {SelectionModel} this
36991              */
36992             "tabend" : true,
36993          /**
36994              * @event beforeeditnext
36995              * Fires before the next editable sell is made active
36996              * You can use this to skip to another cell or fire the tabend
36997              *    if you set cell to false
36998              * @param {Object} eventdata object : { cell : [ row, col ] } 
36999              */
37000             "beforeeditnext" : true
37001     });
37002     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
37003 };
37004
37005 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
37006     
37007     enter_is_tab: false,
37008
37009     /** @ignore */
37010     initEvents : function(){
37011         this.grid.on("mousedown", this.handleMouseDown, this);
37012         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
37013         var view = this.grid.view;
37014         view.on("refresh", this.onViewChange, this);
37015         view.on("rowupdated", this.onRowUpdated, this);
37016         view.on("beforerowremoved", this.clearSelections, this);
37017         view.on("beforerowsinserted", this.clearSelections, this);
37018         if(this.grid.isEditor){
37019             this.grid.on("beforeedit", this.beforeEdit,  this);
37020         }
37021     },
37022
37023         //private
37024     beforeEdit : function(e){
37025         this.select(e.row, e.column, false, true, e.record);
37026     },
37027
37028         //private
37029     onRowUpdated : function(v, index, r){
37030         if(this.selection && this.selection.record == r){
37031             v.onCellSelect(index, this.selection.cell[1]);
37032         }
37033     },
37034
37035         //private
37036     onViewChange : function(){
37037         this.clearSelections(true);
37038     },
37039
37040         /**
37041          * Returns the currently selected cell,.
37042          * @return {Array} The selected cell (row, column) or null if none selected.
37043          */
37044     getSelectedCell : function(){
37045         return this.selection ? this.selection.cell : null;
37046     },
37047
37048     /**
37049      * Clears all selections.
37050      * @param {Boolean} true to prevent the gridview from being notified about the change.
37051      */
37052     clearSelections : function(preventNotify){
37053         var s = this.selection;
37054         if(s){
37055             if(preventNotify !== true){
37056                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
37057             }
37058             this.selection = null;
37059             this.fireEvent("selectionchange", this, null);
37060         }
37061     },
37062
37063     /**
37064      * Returns true if there is a selection.
37065      * @return {Boolean}
37066      */
37067     hasSelection : function(){
37068         return this.selection ? true : false;
37069     },
37070
37071     /** @ignore */
37072     handleMouseDown : function(e, t){
37073         var v = this.grid.getView();
37074         if(this.isLocked()){
37075             return;
37076         };
37077         var row = v.findRowIndex(t);
37078         var cell = v.findCellIndex(t);
37079         if(row !== false && cell !== false){
37080             this.select(row, cell);
37081         }
37082     },
37083
37084     /**
37085      * Selects a cell.
37086      * @param {Number} rowIndex
37087      * @param {Number} collIndex
37088      */
37089     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
37090         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
37091             this.clearSelections();
37092             r = r || this.grid.dataSource.getAt(rowIndex);
37093             this.selection = {
37094                 record : r,
37095                 cell : [rowIndex, colIndex]
37096             };
37097             if(!preventViewNotify){
37098                 var v = this.grid.getView();
37099                 v.onCellSelect(rowIndex, colIndex);
37100                 if(preventFocus !== true){
37101                     v.focusCell(rowIndex, colIndex);
37102                 }
37103             }
37104             this.fireEvent("cellselect", this, rowIndex, colIndex);
37105             this.fireEvent("selectionchange", this, this.selection);
37106         }
37107     },
37108
37109         //private
37110     isSelectable : function(rowIndex, colIndex, cm){
37111         return !cm.isHidden(colIndex);
37112     },
37113
37114     /** @ignore */
37115     handleKeyDown : function(e){
37116         //Roo.log('Cell Sel Model handleKeyDown');
37117         if(!e.isNavKeyPress()){
37118             return;
37119         }
37120         var g = this.grid, s = this.selection;
37121         if(!s){
37122             e.stopEvent();
37123             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
37124             if(cell){
37125                 this.select(cell[0], cell[1]);
37126             }
37127             return;
37128         }
37129         var sm = this;
37130         var walk = function(row, col, step){
37131             return g.walkCells(row, col, step, sm.isSelectable,  sm);
37132         };
37133         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
37134         var newCell;
37135
37136       
37137
37138         switch(k){
37139             case e.TAB:
37140                 // handled by onEditorKey
37141                 if (g.isEditor && g.editing) {
37142                     return;
37143                 }
37144                 if(e.shiftKey) {
37145                     newCell = walk(r, c-1, -1);
37146                 } else {
37147                     newCell = walk(r, c+1, 1);
37148                 }
37149                 break;
37150             
37151             case e.DOWN:
37152                newCell = walk(r+1, c, 1);
37153                 break;
37154             
37155             case e.UP:
37156                 newCell = walk(r-1, c, -1);
37157                 break;
37158             
37159             case e.RIGHT:
37160                 newCell = walk(r, c+1, 1);
37161                 break;
37162             
37163             case e.LEFT:
37164                 newCell = walk(r, c-1, -1);
37165                 break;
37166             
37167             case e.ENTER:
37168                 
37169                 if(g.isEditor && !g.editing){
37170                    g.startEditing(r, c);
37171                    e.stopEvent();
37172                    return;
37173                 }
37174                 
37175                 
37176              break;
37177         };
37178         if(newCell){
37179             this.select(newCell[0], newCell[1]);
37180             e.stopEvent();
37181             
37182         }
37183     },
37184
37185     acceptsNav : function(row, col, cm){
37186         return !cm.isHidden(col) && cm.isCellEditable(col, row);
37187     },
37188     /**
37189      * Selects a cell.
37190      * @param {Number} field (not used) - as it's normally used as a listener
37191      * @param {Number} e - event - fake it by using
37192      *
37193      * var e = Roo.EventObjectImpl.prototype;
37194      * e.keyCode = e.TAB
37195      *
37196      * 
37197      */
37198     onEditorKey : function(field, e){
37199         
37200         var k = e.getKey(),
37201             newCell,
37202             g = this.grid,
37203             ed = g.activeEditor,
37204             forward = false;
37205         ///Roo.log('onEditorKey' + k);
37206         
37207         
37208         if (this.enter_is_tab && k == e.ENTER) {
37209             k = e.TAB;
37210         }
37211         
37212         if(k == e.TAB){
37213             if(e.shiftKey){
37214                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37215             }else{
37216                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37217                 forward = true;
37218             }
37219             
37220             e.stopEvent();
37221             
37222         } else if(k == e.ENTER &&  !e.ctrlKey){
37223             ed.completeEdit();
37224             e.stopEvent();
37225             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37226         
37227                 } else if(k == e.ESC){
37228             ed.cancelEdit();
37229         }
37230                 
37231         if (newCell) {
37232             var ecall = { cell : newCell, forward : forward };
37233             this.fireEvent('beforeeditnext', ecall );
37234             newCell = ecall.cell;
37235                         forward = ecall.forward;
37236         }
37237                 
37238         if(newCell){
37239             //Roo.log('next cell after edit');
37240             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
37241         } else if (forward) {
37242             // tabbed past last
37243             this.fireEvent.defer(100, this, ['tabend',this]);
37244         }
37245     }
37246 });/*
37247  * Based on:
37248  * Ext JS Library 1.1.1
37249  * Copyright(c) 2006-2007, Ext JS, LLC.
37250  *
37251  * Originally Released Under LGPL - original licence link has changed is not relivant.
37252  *
37253  * Fork - LGPL
37254  * <script type="text/javascript">
37255  */
37256  
37257 /**
37258  * @class Roo.grid.EditorGrid
37259  * @extends Roo.grid.Grid
37260  * Class for creating and editable grid.
37261  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
37262  * The container MUST have some type of size defined for the grid to fill. The container will be 
37263  * automatically set to position relative if it isn't already.
37264  * @param {Object} dataSource The data model to bind to
37265  * @param {Object} colModel The column model with info about this grid's columns
37266  */
37267 Roo.grid.EditorGrid = function(container, config){
37268     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
37269     this.getGridEl().addClass("xedit-grid");
37270
37271     if(!this.selModel){
37272         this.selModel = new Roo.grid.CellSelectionModel();
37273     }
37274
37275     this.activeEditor = null;
37276
37277         this.addEvents({
37278             /**
37279              * @event beforeedit
37280              * Fires before cell editing is triggered. The edit event object has the following properties <br />
37281              * <ul style="padding:5px;padding-left:16px;">
37282              * <li>grid - This grid</li>
37283              * <li>record - The record being edited</li>
37284              * <li>field - The field name being edited</li>
37285              * <li>value - The value for the field being edited.</li>
37286              * <li>row - The grid row index</li>
37287              * <li>column - The grid column index</li>
37288              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37289              * </ul>
37290              * @param {Object} e An edit event (see above for description)
37291              */
37292             "beforeedit" : true,
37293             /**
37294              * @event afteredit
37295              * Fires after a cell is edited. <br />
37296              * <ul style="padding:5px;padding-left:16px;">
37297              * <li>grid - This grid</li>
37298              * <li>record - The record being edited</li>
37299              * <li>field - The field name being edited</li>
37300              * <li>value - The value being set</li>
37301              * <li>originalValue - The original value for the field, before the edit.</li>
37302              * <li>row - The grid row index</li>
37303              * <li>column - The grid column index</li>
37304              * </ul>
37305              * @param {Object} e An edit event (see above for description)
37306              */
37307             "afteredit" : true,
37308             /**
37309              * @event validateedit
37310              * Fires after a cell is edited, but before the value is set in the record. 
37311          * You can use this to modify the value being set in the field, Return false
37312              * to cancel the change. The edit event object has the following properties <br />
37313              * <ul style="padding:5px;padding-left:16px;">
37314          * <li>editor - This editor</li>
37315              * <li>grid - This grid</li>
37316              * <li>record - The record being edited</li>
37317              * <li>field - The field name being edited</li>
37318              * <li>value - The value being set</li>
37319              * <li>originalValue - The original value for the field, before the edit.</li>
37320              * <li>row - The grid row index</li>
37321              * <li>column - The grid column index</li>
37322              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37323              * </ul>
37324              * @param {Object} e An edit event (see above for description)
37325              */
37326             "validateedit" : true
37327         });
37328     this.on("bodyscroll", this.stopEditing,  this);
37329     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
37330 };
37331
37332 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
37333     /**
37334      * @cfg {Number} clicksToEdit
37335      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
37336      */
37337     clicksToEdit: 2,
37338
37339     // private
37340     isEditor : true,
37341     // private
37342     trackMouseOver: false, // causes very odd FF errors
37343
37344     onCellDblClick : function(g, row, col){
37345         this.startEditing(row, col);
37346     },
37347
37348     onEditComplete : function(ed, value, startValue){
37349         this.editing = false;
37350         this.activeEditor = null;
37351         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
37352         var r = ed.record;
37353         var field = this.colModel.getDataIndex(ed.col);
37354         var e = {
37355             grid: this,
37356             record: r,
37357             field: field,
37358             originalValue: startValue,
37359             value: value,
37360             row: ed.row,
37361             column: ed.col,
37362             cancel:false,
37363             editor: ed
37364         };
37365         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
37366         cell.show();
37367           
37368         if(String(value) !== String(startValue)){
37369             
37370             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
37371                 r.set(field, e.value);
37372                 // if we are dealing with a combo box..
37373                 // then we also set the 'name' colum to be the displayField
37374                 if (ed.field.displayField && ed.field.name) {
37375                     r.set(ed.field.name, ed.field.el.dom.value);
37376                 }
37377                 
37378                 delete e.cancel; //?? why!!!
37379                 this.fireEvent("afteredit", e);
37380             }
37381         } else {
37382             this.fireEvent("afteredit", e); // always fire it!
37383         }
37384         this.view.focusCell(ed.row, ed.col);
37385     },
37386
37387     /**
37388      * Starts editing the specified for the specified row/column
37389      * @param {Number} rowIndex
37390      * @param {Number} colIndex
37391      */
37392     startEditing : function(row, col){
37393         this.stopEditing();
37394         if(this.colModel.isCellEditable(col, row)){
37395             this.view.ensureVisible(row, col, true);
37396           
37397             var r = this.dataSource.getAt(row);
37398             var field = this.colModel.getDataIndex(col);
37399             var cell = Roo.get(this.view.getCell(row,col));
37400             var e = {
37401                 grid: this,
37402                 record: r,
37403                 field: field,
37404                 value: r.data[field],
37405                 row: row,
37406                 column: col,
37407                 cancel:false 
37408             };
37409             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
37410                 this.editing = true;
37411                 var ed = this.colModel.getCellEditor(col, row);
37412                 
37413                 if (!ed) {
37414                     return;
37415                 }
37416                 if(!ed.rendered){
37417                     ed.render(ed.parentEl || document.body);
37418                 }
37419                 ed.field.reset();
37420                
37421                 cell.hide();
37422                 
37423                 (function(){ // complex but required for focus issues in safari, ie and opera
37424                     ed.row = row;
37425                     ed.col = col;
37426                     ed.record = r;
37427                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
37428                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
37429                     this.activeEditor = ed;
37430                     var v = r.data[field];
37431                     ed.startEdit(this.view.getCell(row, col), v);
37432                     // combo's with 'displayField and name set
37433                     if (ed.field.displayField && ed.field.name) {
37434                         ed.field.el.dom.value = r.data[ed.field.name];
37435                     }
37436                     
37437                     
37438                 }).defer(50, this);
37439             }
37440         }
37441     },
37442         
37443     /**
37444      * Stops any active editing
37445      */
37446     stopEditing : function(){
37447         if(this.activeEditor){
37448             this.activeEditor.completeEdit();
37449         }
37450         this.activeEditor = null;
37451     }
37452 });/*
37453  * Based on:
37454  * Ext JS Library 1.1.1
37455  * Copyright(c) 2006-2007, Ext JS, LLC.
37456  *
37457  * Originally Released Under LGPL - original licence link has changed is not relivant.
37458  *
37459  * Fork - LGPL
37460  * <script type="text/javascript">
37461  */
37462
37463 // private - not really -- you end up using it !
37464 // This is a support class used internally by the Grid components
37465
37466 /**
37467  * @class Roo.grid.GridEditor
37468  * @extends Roo.Editor
37469  * Class for creating and editable grid elements.
37470  * @param {Object} config any settings (must include field)
37471  */
37472 Roo.grid.GridEditor = function(field, config){
37473     if (!config && field.field) {
37474         config = field;
37475         field = Roo.factory(config.field, Roo.form);
37476     }
37477     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
37478     field.monitorTab = false;
37479 };
37480
37481 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
37482     
37483     /**
37484      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
37485      */
37486     
37487     alignment: "tl-tl",
37488     autoSize: "width",
37489     hideEl : false,
37490     cls: "x-small-editor x-grid-editor",
37491     shim:false,
37492     shadow:"frame"
37493 });/*
37494  * Based on:
37495  * Ext JS Library 1.1.1
37496  * Copyright(c) 2006-2007, Ext JS, LLC.
37497  *
37498  * Originally Released Under LGPL - original licence link has changed is not relivant.
37499  *
37500  * Fork - LGPL
37501  * <script type="text/javascript">
37502  */
37503   
37504
37505   
37506 Roo.grid.PropertyRecord = Roo.data.Record.create([
37507     {name:'name',type:'string'},  'value'
37508 ]);
37509
37510
37511 Roo.grid.PropertyStore = function(grid, source){
37512     this.grid = grid;
37513     this.store = new Roo.data.Store({
37514         recordType : Roo.grid.PropertyRecord
37515     });
37516     this.store.on('update', this.onUpdate,  this);
37517     if(source){
37518         this.setSource(source);
37519     }
37520     Roo.grid.PropertyStore.superclass.constructor.call(this);
37521 };
37522
37523
37524
37525 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37526     setSource : function(o){
37527         this.source = o;
37528         this.store.removeAll();
37529         var data = [];
37530         for(var k in o){
37531             if(this.isEditableValue(o[k])){
37532                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37533             }
37534         }
37535         this.store.loadRecords({records: data}, {}, true);
37536     },
37537
37538     onUpdate : function(ds, record, type){
37539         if(type == Roo.data.Record.EDIT){
37540             var v = record.data['value'];
37541             var oldValue = record.modified['value'];
37542             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37543                 this.source[record.id] = v;
37544                 record.commit();
37545                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37546             }else{
37547                 record.reject();
37548             }
37549         }
37550     },
37551
37552     getProperty : function(row){
37553        return this.store.getAt(row);
37554     },
37555
37556     isEditableValue: function(val){
37557         if(val && val instanceof Date){
37558             return true;
37559         }else if(typeof val == 'object' || typeof val == 'function'){
37560             return false;
37561         }
37562         return true;
37563     },
37564
37565     setValue : function(prop, value){
37566         this.source[prop] = value;
37567         this.store.getById(prop).set('value', value);
37568     },
37569
37570     getSource : function(){
37571         return this.source;
37572     }
37573 });
37574
37575 Roo.grid.PropertyColumnModel = function(grid, store){
37576     this.grid = grid;
37577     var g = Roo.grid;
37578     g.PropertyColumnModel.superclass.constructor.call(this, [
37579         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37580         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37581     ]);
37582     this.store = store;
37583     this.bselect = Roo.DomHelper.append(document.body, {
37584         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37585             {tag: 'option', value: 'true', html: 'true'},
37586             {tag: 'option', value: 'false', html: 'false'}
37587         ]
37588     });
37589     Roo.id(this.bselect);
37590     var f = Roo.form;
37591     this.editors = {
37592         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37593         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37594         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37595         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37596         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37597     };
37598     this.renderCellDelegate = this.renderCell.createDelegate(this);
37599     this.renderPropDelegate = this.renderProp.createDelegate(this);
37600 };
37601
37602 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37603     
37604     
37605     nameText : 'Name',
37606     valueText : 'Value',
37607     
37608     dateFormat : 'm/j/Y',
37609     
37610     
37611     renderDate : function(dateVal){
37612         return dateVal.dateFormat(this.dateFormat);
37613     },
37614
37615     renderBool : function(bVal){
37616         return bVal ? 'true' : 'false';
37617     },
37618
37619     isCellEditable : function(colIndex, rowIndex){
37620         return colIndex == 1;
37621     },
37622
37623     getRenderer : function(col){
37624         return col == 1 ?
37625             this.renderCellDelegate : this.renderPropDelegate;
37626     },
37627
37628     renderProp : function(v){
37629         return this.getPropertyName(v);
37630     },
37631
37632     renderCell : function(val){
37633         var rv = val;
37634         if(val instanceof Date){
37635             rv = this.renderDate(val);
37636         }else if(typeof val == 'boolean'){
37637             rv = this.renderBool(val);
37638         }
37639         return Roo.util.Format.htmlEncode(rv);
37640     },
37641
37642     getPropertyName : function(name){
37643         var pn = this.grid.propertyNames;
37644         return pn && pn[name] ? pn[name] : name;
37645     },
37646
37647     getCellEditor : function(colIndex, rowIndex){
37648         var p = this.store.getProperty(rowIndex);
37649         var n = p.data['name'], val = p.data['value'];
37650         
37651         if(typeof(this.grid.customEditors[n]) == 'string'){
37652             return this.editors[this.grid.customEditors[n]];
37653         }
37654         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37655             return this.grid.customEditors[n];
37656         }
37657         if(val instanceof Date){
37658             return this.editors['date'];
37659         }else if(typeof val == 'number'){
37660             return this.editors['number'];
37661         }else if(typeof val == 'boolean'){
37662             return this.editors['boolean'];
37663         }else{
37664             return this.editors['string'];
37665         }
37666     }
37667 });
37668
37669 /**
37670  * @class Roo.grid.PropertyGrid
37671  * @extends Roo.grid.EditorGrid
37672  * This class represents the  interface of a component based property grid control.
37673  * <br><br>Usage:<pre><code>
37674  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37675       
37676  });
37677  // set any options
37678  grid.render();
37679  * </code></pre>
37680   
37681  * @constructor
37682  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37683  * The container MUST have some type of size defined for the grid to fill. The container will be
37684  * automatically set to position relative if it isn't already.
37685  * @param {Object} config A config object that sets properties on this grid.
37686  */
37687 Roo.grid.PropertyGrid = function(container, config){
37688     config = config || {};
37689     var store = new Roo.grid.PropertyStore(this);
37690     this.store = store;
37691     var cm = new Roo.grid.PropertyColumnModel(this, store);
37692     store.store.sort('name', 'ASC');
37693     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37694         ds: store.store,
37695         cm: cm,
37696         enableColLock:false,
37697         enableColumnMove:false,
37698         stripeRows:false,
37699         trackMouseOver: false,
37700         clicksToEdit:1
37701     }, config));
37702     this.getGridEl().addClass('x-props-grid');
37703     this.lastEditRow = null;
37704     this.on('columnresize', this.onColumnResize, this);
37705     this.addEvents({
37706          /**
37707              * @event beforepropertychange
37708              * Fires before a property changes (return false to stop?)
37709              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37710              * @param {String} id Record Id
37711              * @param {String} newval New Value
37712          * @param {String} oldval Old Value
37713              */
37714         "beforepropertychange": true,
37715         /**
37716              * @event propertychange
37717              * Fires after a property changes
37718              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37719              * @param {String} id Record Id
37720              * @param {String} newval New Value
37721          * @param {String} oldval Old Value
37722              */
37723         "propertychange": true
37724     });
37725     this.customEditors = this.customEditors || {};
37726 };
37727 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37728     
37729      /**
37730      * @cfg {Object} customEditors map of colnames=> custom editors.
37731      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37732      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37733      * false disables editing of the field.
37734          */
37735     
37736       /**
37737      * @cfg {Object} propertyNames map of property Names to their displayed value
37738          */
37739     
37740     render : function(){
37741         Roo.grid.PropertyGrid.superclass.render.call(this);
37742         this.autoSize.defer(100, this);
37743     },
37744
37745     autoSize : function(){
37746         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37747         if(this.view){
37748             this.view.fitColumns();
37749         }
37750     },
37751
37752     onColumnResize : function(){
37753         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37754         this.autoSize();
37755     },
37756     /**
37757      * Sets the data for the Grid
37758      * accepts a Key => Value object of all the elements avaiable.
37759      * @param {Object} data  to appear in grid.
37760      */
37761     setSource : function(source){
37762         this.store.setSource(source);
37763         //this.autoSize();
37764     },
37765     /**
37766      * Gets all the data from the grid.
37767      * @return {Object} data  data stored in grid
37768      */
37769     getSource : function(){
37770         return this.store.getSource();
37771     }
37772 });/*
37773  * Based on:
37774  * Ext JS Library 1.1.1
37775  * Copyright(c) 2006-2007, Ext JS, LLC.
37776  *
37777  * Originally Released Under LGPL - original licence link has changed is not relivant.
37778  *
37779  * Fork - LGPL
37780  * <script type="text/javascript">
37781  */
37782  
37783 /**
37784  * @class Roo.LoadMask
37785  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37786  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37787  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37788  * element's UpdateManager load indicator and will be destroyed after the initial load.
37789  * @constructor
37790  * Create a new LoadMask
37791  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37792  * @param {Object} config The config object
37793  */
37794 Roo.LoadMask = function(el, config){
37795     this.el = Roo.get(el);
37796     Roo.apply(this, config);
37797     if(this.store){
37798         this.store.on('beforeload', this.onBeforeLoad, this);
37799         this.store.on('load', this.onLoad, this);
37800         this.store.on('loadexception', this.onLoadException, this);
37801         this.removeMask = false;
37802     }else{
37803         var um = this.el.getUpdateManager();
37804         um.showLoadIndicator = false; // disable the default indicator
37805         um.on('beforeupdate', this.onBeforeLoad, this);
37806         um.on('update', this.onLoad, this);
37807         um.on('failure', this.onLoad, this);
37808         this.removeMask = true;
37809     }
37810 };
37811
37812 Roo.LoadMask.prototype = {
37813     /**
37814      * @cfg {Boolean} removeMask
37815      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37816      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37817      */
37818     /**
37819      * @cfg {String} msg
37820      * The text to display in a centered loading message box (defaults to 'Loading...')
37821      */
37822     msg : 'Loading...',
37823     /**
37824      * @cfg {String} msgCls
37825      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37826      */
37827     msgCls : 'x-mask-loading',
37828
37829     /**
37830      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37831      * @type Boolean
37832      */
37833     disabled: false,
37834
37835     /**
37836      * Disables the mask to prevent it from being displayed
37837      */
37838     disable : function(){
37839        this.disabled = true;
37840     },
37841
37842     /**
37843      * Enables the mask so that it can be displayed
37844      */
37845     enable : function(){
37846         this.disabled = false;
37847     },
37848     
37849     onLoadException : function()
37850     {
37851         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37852             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37853         }
37854         this.el.unmask(this.removeMask);
37855     },
37856     // private
37857     onLoad : function()
37858     {
37859         this.el.unmask(this.removeMask);
37860     },
37861
37862     // private
37863     onBeforeLoad : function(){
37864         if(!this.disabled){
37865             this.el.mask(this.msg, this.msgCls);
37866         }
37867     },
37868
37869     // private
37870     destroy : function(){
37871         if(this.store){
37872             this.store.un('beforeload', this.onBeforeLoad, this);
37873             this.store.un('load', this.onLoad, this);
37874             this.store.un('loadexception', this.onLoadException, this);
37875         }else{
37876             var um = this.el.getUpdateManager();
37877             um.un('beforeupdate', this.onBeforeLoad, this);
37878             um.un('update', this.onLoad, this);
37879             um.un('failure', this.onLoad, this);
37880         }
37881     }
37882 };/*
37883  * Based on:
37884  * Ext JS Library 1.1.1
37885  * Copyright(c) 2006-2007, Ext JS, LLC.
37886  *
37887  * Originally Released Under LGPL - original licence link has changed is not relivant.
37888  *
37889  * Fork - LGPL
37890  * <script type="text/javascript">
37891  */
37892
37893
37894 /**
37895  * @class Roo.XTemplate
37896  * @extends Roo.Template
37897  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
37898 <pre><code>
37899 var t = new Roo.XTemplate(
37900         '&lt;select name="{name}"&gt;',
37901                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
37902         '&lt;/select&gt;'
37903 );
37904  
37905 // then append, applying the master template values
37906  </code></pre>
37907  *
37908  * Supported features:
37909  *
37910  *  Tags:
37911
37912 <pre><code>
37913       {a_variable} - output encoded.
37914       {a_variable.format:("Y-m-d")} - call a method on the variable
37915       {a_variable:raw} - unencoded output
37916       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
37917       {a_variable:this.method_on_template(...)} - call a method on the template object.
37918  
37919 </code></pre>
37920  *  The tpl tag:
37921 <pre><code>
37922         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
37923         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
37924         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
37925         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
37926   
37927         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
37928         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
37929 </code></pre>
37930  *      
37931  */
37932 Roo.XTemplate = function()
37933 {
37934     Roo.XTemplate.superclass.constructor.apply(this, arguments);
37935     if (this.html) {
37936         this.compile();
37937     }
37938 };
37939
37940
37941 Roo.extend(Roo.XTemplate, Roo.Template, {
37942
37943     /**
37944      * The various sub templates
37945      */
37946     tpls : false,
37947     /**
37948      *
37949      * basic tag replacing syntax
37950      * WORD:WORD()
37951      *
37952      * // you can fake an object call by doing this
37953      *  x.t:(test,tesT) 
37954      * 
37955      */
37956     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
37957
37958     /**
37959      * compile the template
37960      *
37961      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
37962      *
37963      */
37964     compile: function()
37965     {
37966         var s = this.html;
37967      
37968         s = ['<tpl>', s, '</tpl>'].join('');
37969     
37970         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
37971             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
37972             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
37973             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
37974             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
37975             m,
37976             id     = 0,
37977             tpls   = [];
37978     
37979         while(true == !!(m = s.match(re))){
37980             var forMatch   = m[0].match(nameRe),
37981                 ifMatch   = m[0].match(ifRe),
37982                 execMatch   = m[0].match(execRe),
37983                 namedMatch   = m[0].match(namedRe),
37984                 
37985                 exp  = null, 
37986                 fn   = null,
37987                 exec = null,
37988                 name = forMatch && forMatch[1] ? forMatch[1] : '';
37989                 
37990             if (ifMatch) {
37991                 // if - puts fn into test..
37992                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
37993                 if(exp){
37994                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
37995                 }
37996             }
37997             
37998             if (execMatch) {
37999                 // exec - calls a function... returns empty if true is  returned.
38000                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38001                 if(exp){
38002                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38003                 }
38004             }
38005             
38006             
38007             if (name) {
38008                 // for = 
38009                 switch(name){
38010                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38011                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38012                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38013                 }
38014             }
38015             var uid = namedMatch ? namedMatch[1] : id;
38016             
38017             
38018             tpls.push({
38019                 id:     namedMatch ? namedMatch[1] : id,
38020                 target: name,
38021                 exec:   exec,
38022                 test:   fn,
38023                 body:   m[1] || ''
38024             });
38025             if (namedMatch) {
38026                 s = s.replace(m[0], '');
38027             } else { 
38028                 s = s.replace(m[0], '{xtpl'+ id + '}');
38029             }
38030             ++id;
38031         }
38032         this.tpls = [];
38033         for(var i = tpls.length-1; i >= 0; --i){
38034             this.compileTpl(tpls[i]);
38035             this.tpls[tpls[i].id] = tpls[i];
38036         }
38037         this.master = tpls[tpls.length-1];
38038         return this;
38039     },
38040     /**
38041      * same as applyTemplate, except it's done to one of the subTemplates
38042      * when using named templates, you can do:
38043      *
38044      * var str = pl.applySubTemplate('your-name', values);
38045      *
38046      * 
38047      * @param {Number} id of the template
38048      * @param {Object} values to apply to template
38049      * @param {Object} parent (normaly the instance of this object)
38050      */
38051     applySubTemplate : function(id, values, parent)
38052     {
38053         
38054         
38055         var t = this.tpls[id];
38056         
38057         
38058         try { 
38059             if(t.test && !t.test.call(this, values, parent)){
38060                 return '';
38061             }
38062         } catch(e) {
38063             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38064             Roo.log(e.toString());
38065             Roo.log(t.test);
38066             return ''
38067         }
38068         try { 
38069             
38070             if(t.exec && t.exec.call(this, values, parent)){
38071                 return '';
38072             }
38073         } catch(e) {
38074             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38075             Roo.log(e.toString());
38076             Roo.log(t.exec);
38077             return ''
38078         }
38079         try {
38080             var vs = t.target ? t.target.call(this, values, parent) : values;
38081             parent = t.target ? values : parent;
38082             if(t.target && vs instanceof Array){
38083                 var buf = [];
38084                 for(var i = 0, len = vs.length; i < len; i++){
38085                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38086                 }
38087                 return buf.join('');
38088             }
38089             return t.compiled.call(this, vs, parent);
38090         } catch (e) {
38091             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38092             Roo.log(e.toString());
38093             Roo.log(t.compiled);
38094             return '';
38095         }
38096     },
38097
38098     compileTpl : function(tpl)
38099     {
38100         var fm = Roo.util.Format;
38101         var useF = this.disableFormats !== true;
38102         var sep = Roo.isGecko ? "+" : ",";
38103         var undef = function(str) {
38104             Roo.log("Property not found :"  + str);
38105             return '';
38106         };
38107         
38108         var fn = function(m, name, format, args)
38109         {
38110             //Roo.log(arguments);
38111             args = args ? args.replace(/\\'/g,"'") : args;
38112             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38113             if (typeof(format) == 'undefined') {
38114                 format= 'htmlEncode';
38115             }
38116             if (format == 'raw' ) {
38117                 format = false;
38118             }
38119             
38120             if(name.substr(0, 4) == 'xtpl'){
38121                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38122             }
38123             
38124             // build an array of options to determine if value is undefined..
38125             
38126             // basically get 'xxxx.yyyy' then do
38127             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38128             //    (function () { Roo.log("Property not found"); return ''; })() :
38129             //    ......
38130             
38131             var udef_ar = [];
38132             var lookfor = '';
38133             Roo.each(name.split('.'), function(st) {
38134                 lookfor += (lookfor.length ? '.': '') + st;
38135                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38136             });
38137             
38138             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38139             
38140             
38141             if(format && useF){
38142                 
38143                 args = args ? ',' + args : "";
38144                  
38145                 if(format.substr(0, 5) != "this."){
38146                     format = "fm." + format + '(';
38147                 }else{
38148                     format = 'this.call("'+ format.substr(5) + '", ';
38149                     args = ", values";
38150                 }
38151                 
38152                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38153             }
38154              
38155             if (args.length) {
38156                 // called with xxyx.yuu:(test,test)
38157                 // change to ()
38158                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38159             }
38160             // raw.. - :raw modifier..
38161             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38162             
38163         };
38164         var body;
38165         // branched to use + in gecko and [].join() in others
38166         if(Roo.isGecko){
38167             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38168                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38169                     "';};};";
38170         }else{
38171             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38172             body.push(tpl.body.replace(/(\r\n|\n)/g,
38173                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38174             body.push("'].join('');};};");
38175             body = body.join('');
38176         }
38177         
38178         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38179        
38180         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38181         eval(body);
38182         
38183         return this;
38184     },
38185
38186     applyTemplate : function(values){
38187         return this.master.compiled.call(this, values, {});
38188         //var s = this.subs;
38189     },
38190
38191     apply : function(){
38192         return this.applyTemplate.apply(this, arguments);
38193     }
38194
38195  });
38196
38197 Roo.XTemplate.from = function(el){
38198     el = Roo.getDom(el);
38199     return new Roo.XTemplate(el.value || el.innerHTML);
38200 };/*
38201  * Original code for Roojs - LGPL
38202  * <script type="text/javascript">
38203  */
38204  
38205 /**
38206  * @class Roo.XComponent
38207  * A delayed Element creator...
38208  * Or a way to group chunks of interface together.
38209  * 
38210  * Mypart.xyx = new Roo.XComponent({
38211
38212     parent : 'Mypart.xyz', // empty == document.element.!!
38213     order : '001',
38214     name : 'xxxx'
38215     region : 'xxxx'
38216     disabled : function() {} 
38217      
38218     tree : function() { // return an tree of xtype declared components
38219         var MODULE = this;
38220         return 
38221         {
38222             xtype : 'NestedLayoutPanel',
38223             // technicall
38224         }
38225      ]
38226  *})
38227  *
38228  *
38229  * It can be used to build a big heiracy, with parent etc.
38230  * or you can just use this to render a single compoent to a dom element
38231  * MYPART.render(Roo.Element | String(id) | dom_element )
38232  * 
38233  * @extends Roo.util.Observable
38234  * @constructor
38235  * @param cfg {Object} configuration of component
38236  * 
38237  */
38238 Roo.XComponent = function(cfg) {
38239     Roo.apply(this, cfg);
38240     this.addEvents({ 
38241         /**
38242              * @event built
38243              * Fires when this the componnt is built
38244              * @param {Roo.XComponent} c the component
38245              */
38246         'built' : true
38247         
38248     });
38249     this.region = this.region || 'center'; // default..
38250     Roo.XComponent.register(this);
38251     this.modules = false;
38252     this.el = false; // where the layout goes..
38253     
38254     
38255 }
38256 Roo.extend(Roo.XComponent, Roo.util.Observable, {
38257     /**
38258      * @property el
38259      * The created element (with Roo.factory())
38260      * @type {Roo.Layout}
38261      */
38262     el  : false,
38263     
38264     /**
38265      * @property el
38266      * for BC  - use el in new code
38267      * @type {Roo.Layout}
38268      */
38269     panel : false,
38270     
38271     /**
38272      * @property layout
38273      * for BC  - use el in new code
38274      * @type {Roo.Layout}
38275      */
38276     layout : false,
38277     
38278      /**
38279      * @cfg {Function|boolean} disabled
38280      * If this module is disabled by some rule, return true from the funtion
38281      */
38282     disabled : false,
38283     
38284     /**
38285      * @cfg {String} parent 
38286      * Name of parent element which it get xtype added to..
38287      */
38288     parent: false,
38289     
38290     /**
38291      * @cfg {String} order
38292      * Used to set the order in which elements are created (usefull for multiple tabs)
38293      */
38294     
38295     order : false,
38296     /**
38297      * @cfg {String} name
38298      * String to display while loading.
38299      */
38300     name : false,
38301     /**
38302      * @cfg {String} region
38303      * Region to render component to (defaults to center)
38304      */
38305     region : 'center',
38306     
38307     /**
38308      * @cfg {Array} items
38309      * A single item array - the first element is the root of the tree..
38310      * It's done this way to stay compatible with the Xtype system...
38311      */
38312     items : false,
38313     
38314     /**
38315      * @property _tree
38316      * The method that retuns the tree of parts that make up this compoennt 
38317      * @type {function}
38318      */
38319     _tree  : false,
38320     
38321      /**
38322      * render
38323      * render element to dom or tree
38324      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
38325      */
38326     
38327     render : function(el)
38328     {
38329         
38330         el = el || false;
38331         var hp = this.parent ? 1 : 0;
38332         
38333         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
38334             // if parent is a '#.....' string, then let's use that..
38335             var ename = this.parent.substr(1)
38336             this.parent = false;
38337             el = Roo.get(ename);
38338             if (!el) {
38339                 Roo.log("Warning - element can not be found :#" + ename );
38340                 return;
38341             }
38342         }
38343         
38344         
38345         if (!this.parent) {
38346             
38347             el = el ? Roo.get(el) : false;      
38348             
38349             // it's a top level one..
38350             this.parent =  {
38351                 el : new Roo.BorderLayout(el || document.body, {
38352                 
38353                      center: {
38354                          titlebar: false,
38355                          autoScroll:false,
38356                          closeOnTab: true,
38357                          tabPosition: 'top',
38358                           //resizeTabs: true,
38359                          alwaysShowTabs: el && hp? false :  true,
38360                          hideTabs: el || !hp ? true :  false,
38361                          minTabWidth: 140
38362                      }
38363                  })
38364             }
38365         }
38366         
38367                 
38368                 // The 'tree' method is  '_tree now' 
38369             
38370         var tree = this._tree ? this._tree() : this.tree();
38371         tree.region = tree.region || this.region;
38372         this.el = this.parent.el.addxtype(tree);
38373         this.fireEvent('built', this);
38374         
38375         this.panel = this.el;
38376         this.layout = this.panel.layout;
38377                 this.parentLayout = this.parent.layout  || false;  
38378          
38379     }
38380     
38381 });
38382
38383 Roo.apply(Roo.XComponent, {
38384     
38385     /**
38386      * @property  buildCompleted
38387      * True when the builder has completed building the interface.
38388      * @type Boolean
38389      */
38390     buildCompleted : false,
38391      
38392     /**
38393      * @property  topModule
38394      * the upper most module - uses document.element as it's constructor.
38395      * @type Object
38396      */
38397      
38398     topModule  : false,
38399       
38400     /**
38401      * @property  modules
38402      * array of modules to be created by registration system.
38403      * @type {Array} of Roo.XComponent
38404      */
38405     
38406     modules : [],
38407     /**
38408      * @property  elmodules
38409      * array of modules to be created by which use #ID 
38410      * @type {Array} of Roo.XComponent
38411      */
38412      
38413     elmodules : [],
38414
38415     
38416     /**
38417      * Register components to be built later.
38418      *
38419      * This solves the following issues
38420      * - Building is not done on page load, but after an authentication process has occured.
38421      * - Interface elements are registered on page load
38422      * - Parent Interface elements may not be loaded before child, so this handles that..
38423      * 
38424      *
38425      * example:
38426      * 
38427      * MyApp.register({
38428           order : '000001',
38429           module : 'Pman.Tab.projectMgr',
38430           region : 'center',
38431           parent : 'Pman.layout',
38432           disabled : false,  // or use a function..
38433         })
38434      
38435      * * @param {Object} details about module
38436      */
38437     register : function(obj) {
38438                 
38439                 Roo.XComponent.event.fireEvent('register', obj);
38440                 switch(typeof(obj.disabled) ) {
38441                         
38442                         case 'undefined':
38443                                 break;
38444                         
38445                         case 'function':
38446                                 if ( obj.disabled() ) {
38447                                         return;
38448                                 }
38449                                 break;
38450                         default:
38451                                 if (obj.disabled) {
38452                                         return;
38453                                 }
38454                                 break;
38455                 }
38456                 
38457         this.modules.push(obj);
38458          
38459     },
38460     /**
38461      * convert a string to an object..
38462      * eg. 'AAA.BBB' -> finds AAA.BBB
38463
38464      */
38465     
38466     toObject : function(str)
38467     {
38468         if (!str || typeof(str) == 'object') {
38469             return str;
38470         }
38471         if (str.substring(0,1) == '#') {
38472             return str;
38473         }
38474
38475         var ar = str.split('.');
38476         var rt, o;
38477         rt = ar.shift();
38478             /** eval:var:o */
38479         try {
38480             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
38481         } catch (e) {
38482             throw "Module not found : " + str;
38483         }
38484         
38485         if (o === false) {
38486             throw "Module not found : " + str;
38487         }
38488         Roo.each(ar, function(e) {
38489             if (typeof(o[e]) == 'undefined') {
38490                 throw "Module not found : " + str;
38491             }
38492             o = o[e];
38493         });
38494         
38495         return o;
38496         
38497     },
38498     
38499     
38500     /**
38501      * move modules into their correct place in the tree..
38502      * 
38503      */
38504     preBuild : function ()
38505     {
38506         var _t = this;
38507         Roo.each(this.modules , function (obj)
38508         {
38509             var opar = obj.parent;
38510             try { 
38511                 obj.parent = this.toObject(opar);
38512             } catch(e) {
38513                 Roo.log("parent:toObject failed: " + e.toString());
38514                 return;
38515             }
38516             
38517             if (!obj.parent) {
38518                                 Roo.debug && Roo.log("GOT top level module");
38519                                 Roo.debug && Roo.log(obj);
38520                                 obj.modules = new Roo.util.MixedCollection(false, 
38521                     function(o) { return o.order + '' }
38522                 );
38523                 this.topModule = obj;
38524                 return;
38525             }
38526                         // parent is a string (usually a dom element name..)
38527             if (typeof(obj.parent) == 'string') {
38528                 this.elmodules.push(obj);
38529                 return;
38530             }
38531             if (obj.parent.constructor != Roo.XComponent) {
38532                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
38533             }
38534             if (!obj.parent.modules) {
38535                 obj.parent.modules = new Roo.util.MixedCollection(false, 
38536                     function(o) { return o.order + '' }
38537                 );
38538             }
38539             
38540             obj.parent.modules.add(obj);
38541         }, this);
38542     },
38543     
38544      /**
38545      * make a list of modules to build.
38546      * @return {Array} list of modules. 
38547      */ 
38548     
38549     buildOrder : function()
38550     {
38551         var _this = this;
38552         var cmp = function(a,b) {   
38553             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
38554         };
38555         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
38556             throw "No top level modules to build";
38557         }
38558         
38559         // make a flat list in order of modules to build.
38560         var mods = this.topModule ? [ this.topModule ] : [];
38561                 
38562                 // elmodules (is a list of DOM based modules )
38563         Roo.each(this.elmodules,function(e) { mods.push(e) });
38564
38565         
38566         // add modules to their parents..
38567         var addMod = function(m) {
38568                         Roo.debug && Roo.log("build Order: add: " + m.name);
38569             
38570             mods.push(m);
38571             if (m.modules) {
38572                                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
38573                 m.modules.keySort('ASC',  cmp );
38574                                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
38575
38576                 m.modules.each(addMod);
38577             } else {
38578                                 Roo.debug && Roo.log("build Order: no child modules");
38579                         }
38580             // not sure if this is used any more..
38581             if (m.finalize) {
38582                 m.finalize.name = m.name + " (clean up) ";
38583                 mods.push(m.finalize);
38584             }
38585             
38586         }
38587         if (this.topModule) { 
38588             this.topModule.modules.keySort('ASC',  cmp );
38589             this.topModule.modules.each(addMod);
38590         }
38591         return mods;
38592     },
38593     
38594      /**
38595      * Build the registered modules.
38596      * @param {Object} parent element.
38597      * @param {Function} optional method to call after module has been added.
38598      * 
38599      */ 
38600    
38601     build : function() 
38602     {
38603         
38604         this.preBuild();
38605         var mods = this.buildOrder();
38606       
38607         //this.allmods = mods;
38608         //Roo.debug && Roo.log(mods);
38609         //return;
38610         if (!mods.length) { // should not happen
38611             throw "NO modules!!!";
38612         }
38613         
38614         
38615         var msg = "Building Interface...";
38616         // flash it up as modal - so we store the mask!?
38617         Roo.MessageBox.show({ title: 'loading' });
38618         Roo.MessageBox.show({
38619            title: "Please wait...",
38620            msg: msg,
38621            width:450,
38622            progress:true,
38623            closable:false,
38624            modal: false
38625           
38626         });
38627         var total = mods.length;
38628         
38629         var _this = this;
38630         var progressRun = function() {
38631             if (!mods.length) {
38632                 Roo.debug && Roo.log('hide?');
38633                 Roo.MessageBox.hide();
38634                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
38635                 
38636                 // THE END...
38637                 return false;   
38638             }
38639             
38640             var m = mods.shift();
38641             
38642             
38643             Roo.debug && Roo.log(m);
38644             // not sure if this is supported any more.. - modules that are are just function
38645             if (typeof(m) == 'function') { 
38646                 m.call(this);
38647                 return progressRun.defer(10, _this);
38648             } 
38649             
38650             
38651             msg = "Building Interface " + (total  - mods.length) + 
38652                     " of " + total + 
38653                     (m.name ? (' - ' + m.name) : '');
38654                         Roo.debug && Roo.log(msg);
38655             Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
38656             
38657          
38658             // is the module disabled?
38659             var disabled = (typeof(m.disabled) == 'function') ?
38660                 m.disabled.call(m.module.disabled) : m.disabled;    
38661             
38662             
38663             if (disabled) {
38664                 return progressRun(); // we do not update the display!
38665             }
38666             
38667             // now build 
38668             
38669                         
38670                         
38671             m.render();
38672             // it's 10 on top level, and 1 on others??? why...
38673             return progressRun.defer(10, _this);
38674              
38675         }
38676         progressRun.defer(1, _this);
38677      
38678         
38679         
38680     },
38681         
38682         
38683         /**
38684          * Event Object.
38685          *
38686          *
38687          */
38688         event: false, 
38689     /**
38690          * wrapper for event.on - aliased later..  
38691          * Typically use to register a event handler for register:
38692          *
38693          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
38694          *
38695          */
38696     on : false
38697    
38698     
38699     
38700 });
38701
38702 Roo.XComponent.event = new Roo.util.Observable({
38703                 events : { 
38704                         /**
38705                          * @event register
38706                          * Fires when an Component is registered,
38707                          * set the disable property on the Component to stop registration.
38708                          * @param {Roo.XComponent} c the component being registerd.
38709                          * 
38710                          */
38711                         'register' : true,
38712                         /**
38713                          * @event buildcomplete
38714                          * Fires on the top level element when all elements have been built
38715                          * @param {Roo.XComponent} the top level component.
38716                          */
38717                         'buildcomplete' : true
38718                         
38719                 }
38720 });
38721
38722 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
38723  //<script type="text/javascript">
38724
38725
38726 /**
38727  * @class Roo.Login
38728  * @extends Roo.LayoutDialog
38729  * A generic Login Dialog..... - only one needed in theory!?!?
38730  *
38731  * Fires XComponent builder on success...
38732  * 
38733  * Sends 
38734  *    username,password, lang = for login actions.
38735  *    check = 1 for periodic checking that sesion is valid.
38736  *    passwordRequest = email request password
38737  *    logout = 1 = to logout
38738  * 
38739  * Affects: (this id="????" elements)
38740  *   loading  (removed) (used to indicate application is loading)
38741  *   loading-mask (hides) (used to hide application when it's building loading)
38742  *   
38743  * 
38744  * Usage: 
38745  *    
38746  * 
38747  * Myapp.login = Roo.Login({
38748      url: xxxx,
38749    
38750      realm : 'Myapp', 
38751      
38752      
38753      method : 'POST',
38754      
38755      
38756      * 
38757  })
38758  * 
38759  * 
38760  * 
38761  **/
38762  
38763 Roo.Login = function(cfg)
38764 {
38765     this.addEvents({
38766         'refreshed' : true
38767     });
38768     
38769     Roo.apply(this,cfg);
38770     
38771     Roo.onReady(function() {
38772         this.onLoad();
38773     }, this);
38774     // call parent..
38775     
38776    
38777     Roo.Login.superclass.constructor.call(this, this);
38778     //this.addxtype(this.items[0]);
38779     
38780     
38781 }
38782
38783
38784 Roo.extend(Roo.Login, Roo.LayoutDialog, {
38785     
38786     /**
38787      * @cfg {String} method
38788      * Method used to query for login details.
38789      */
38790     
38791     method : 'POST',
38792     /**
38793      * @cfg {String} url
38794      * URL to query login data. - eg. baseURL + '/Login.php'
38795      */
38796     url : '',
38797     
38798     /**
38799      * @property user
38800      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
38801      * @type {Object} 
38802      */
38803     user : false,
38804     /**
38805      * @property checkFails
38806      * Number of times we have attempted to get authentication check, and failed.
38807      * @type {Number} 
38808      */
38809     checkFails : 0,
38810       /**
38811      * @property intervalID
38812      * The window interval that does the constant login checking.
38813      * @type {Number} 
38814      */
38815     intervalID : 0,
38816     
38817     
38818     onLoad : function() // called on page load...
38819     {
38820         // load 
38821          
38822         if (Roo.get('loading')) { // clear any loading indicator..
38823             Roo.get('loading').remove();
38824         }
38825         
38826         //this.switchLang('en'); // set the language to english..
38827        
38828         this.check({
38829             success:  function(response, opts)  {  // check successfull...
38830             
38831                 var res = this.processResponse(response);
38832                 this.checkFails =0;
38833                 if (!res.success) { // error!
38834                     this.checkFails = 5;
38835                     //console.log('call failure');
38836                     return this.failure(response,opts);
38837                 }
38838                 
38839                 if (!res.data.id) { // id=0 == login failure.
38840                     return this.show();
38841                 }
38842                 
38843                               
38844                         //console.log(success);
38845                 this.fillAuth(res.data);   
38846                 this.checkFails =0;
38847                 Roo.XComponent.build();
38848             },
38849             failure : this.show
38850         });
38851         
38852     }, 
38853     
38854     
38855     check: function(cfg) // called every so often to refresh cookie etc..
38856     {
38857         if (cfg.again) { // could be undefined..
38858             this.checkFails++;
38859         } else {
38860             this.checkFails = 0;
38861         }
38862         var _this = this;
38863         if (this.sending) {
38864             if ( this.checkFails > 4) {
38865                 Roo.MessageBox.alert("Error",  
38866                     "Error getting authentication status. - try reloading, or wait a while", function() {
38867                         _this.sending = false;
38868                     }); 
38869                 return;
38870             }
38871             cfg.again = true;
38872             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
38873             return;
38874         }
38875         this.sending = true;
38876         
38877         Roo.Ajax.request({  
38878             url: this.url,
38879             params: {
38880                 getAuthUser: true
38881             },  
38882             method: this.method,
38883             success:  cfg.success || this.success,
38884             failure : cfg.failure || this.failure,
38885             scope : this,
38886             callCfg : cfg
38887               
38888         });  
38889     }, 
38890     
38891     
38892     logout: function()
38893     {
38894         window.onbeforeunload = function() { }; // false does not work for IE..
38895         this.user = false;
38896         var _this = this;
38897         
38898         Roo.Ajax.request({  
38899             url: this.url,
38900             params: {
38901                 logout: 1
38902             },  
38903             method: 'GET',
38904             failure : function() {
38905                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
38906                     document.location = document.location.toString() + '?ts=' + Math.random();
38907                 });
38908                 
38909             },
38910             success : function() {
38911                 _this.user = false;
38912                 this.checkFails =0;
38913                 // fixme..
38914                 document.location = document.location.toString() + '?ts=' + Math.random();
38915             }
38916               
38917               
38918         }); 
38919     },
38920     
38921     processResponse : function (response)
38922     {
38923         var res = '';
38924         try {
38925             res = Roo.decode(response.responseText);
38926             // oops...
38927             if (typeof(res) != 'object') {
38928                 res = { success : false, errorMsg : res, errors : true };
38929             }
38930             if (typeof(res.success) == 'undefined') {
38931                 res.success = false;
38932             }
38933             
38934         } catch(e) {
38935             res = { success : false,  errorMsg : response.responseText, errors : true };
38936         }
38937         return res;
38938     },
38939     
38940     success : function(response, opts)  // check successfull...
38941     {  
38942         this.sending = false;
38943         var res = this.processResponse(response);
38944         if (!res.success) {
38945             return this.failure(response, opts);
38946         }
38947         if (!res.data || !res.data.id) {
38948             return this.failure(response,opts);
38949         }
38950         //console.log(res);
38951         this.fillAuth(res.data);
38952         
38953         this.checkFails =0;
38954         
38955     },
38956     
38957     
38958     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
38959     {
38960         this.authUser = -1;
38961         this.sending = false;
38962         var res = this.processResponse(response);
38963         //console.log(res);
38964         if ( this.checkFails > 2) {
38965         
38966             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
38967                 "Error getting authentication status. - try reloading"); 
38968             return;
38969         }
38970         opts.callCfg.again = true;
38971         this.check.defer(1000, this, [ opts.callCfg ]);
38972         return;  
38973     },
38974     
38975     
38976     
38977     fillAuth: function(au) {
38978         this.startAuthCheck();
38979         this.authUserId = au.id;
38980         this.authUser = au;
38981         this.lastChecked = new Date();
38982         this.fireEvent('refreshed', au);
38983         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
38984         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
38985         au.lang = au.lang || 'en';
38986         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
38987         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
38988         this.switchLang(au.lang );
38989         
38990      
38991         // open system... - -on setyp..
38992         if (this.authUserId  < 0) {
38993             Roo.MessageBox.alert("Warning", 
38994                 "This is an open system - please set up a admin user with a password.");  
38995         }
38996          
38997         //Pman.onload(); // which should do nothing if it's a re-auth result...
38998         
38999              
39000     },
39001     
39002     startAuthCheck : function() // starter for timeout checking..
39003     {
39004         if (this.intervalID) { // timer already in place...
39005             return false;
39006         }
39007         var _this = this;
39008         this.intervalID =  window.setInterval(function() {
39009               _this.check(false);
39010             }, 120000); // every 120 secs = 2mins..
39011         
39012         
39013     },
39014          
39015     
39016     switchLang : function (lang) 
39017     {
39018         _T = typeof(_T) == 'undefined' ? false : _T;
39019           if (!_T || !lang.length) {
39020             return;
39021         }
39022         
39023         if (!_T && lang != 'en') {
39024             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
39025             return;
39026         }
39027         
39028         if (typeof(_T.en) == 'undefined') {
39029             _T.en = {};
39030             Roo.apply(_T.en, _T);
39031         }
39032         
39033         if (typeof(_T[lang]) == 'undefined') {
39034             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
39035             return;
39036         }
39037         
39038         
39039         Roo.apply(_T, _T[lang]);
39040         // just need to set the text values for everything...
39041         var _this = this;
39042         /* this will not work ...
39043         if (this.form) { 
39044             
39045                
39046             function formLabel(name, val) {
39047                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
39048             }
39049             
39050             formLabel('password', "Password"+':');
39051             formLabel('username', "Email Address"+':');
39052             formLabel('lang', "Language"+':');
39053             this.dialog.setTitle("Login");
39054             this.dialog.buttons[0].setText("Forgot Password");
39055             this.dialog.buttons[1].setText("Login");
39056         }
39057         */
39058         
39059         
39060     },
39061     
39062     
39063     title: "Login",
39064     modal: true,
39065     width:  350,
39066     //height: 230,
39067     height: 180,
39068     shadow: true,
39069     minWidth:200,
39070     minHeight:180,
39071     //proxyDrag: true,
39072     closable: false,
39073     draggable: false,
39074     collapsible: false,
39075     resizable: false,
39076     center: {  // needed??
39077         autoScroll:false,
39078         titlebar: false,
39079        // tabPosition: 'top',
39080         hideTabs: true,
39081         closeOnTab: true,
39082         alwaysShowTabs: false
39083     } ,
39084     listeners : {
39085         
39086         show  : function(dlg)
39087         {
39088             //console.log(this);
39089             this.form = this.layout.getRegion('center').activePanel.form;
39090             this.form.dialog = dlg;
39091             this.buttons[0].form = this.form;
39092             this.buttons[0].dialog = dlg;
39093             this.buttons[1].form = this.form;
39094             this.buttons[1].dialog = dlg;
39095            
39096            //this.resizeToLogo.defer(1000,this);
39097             // this is all related to resizing for logos..
39098             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
39099            //// if (!sz) {
39100              //   this.resizeToLogo.defer(1000,this);
39101              //   return;
39102            // }
39103             //var w = Ext.lib.Dom.getViewWidth() - 100;
39104             //var h = Ext.lib.Dom.getViewHeight() - 100;
39105             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
39106             //this.center();
39107             if (this.disabled) {
39108                 this.hide();
39109                 return;
39110             }
39111             
39112             if (this.user.id < 0) { // used for inital setup situations.
39113                 return;
39114             }
39115             
39116             if (this.intervalID) {
39117                 // remove the timer
39118                 window.clearInterval(this.intervalID);
39119                 this.intervalID = false;
39120             }
39121             
39122             
39123             if (Roo.get('loading')) {
39124                 Roo.get('loading').remove();
39125             }
39126             if (Roo.get('loading-mask')) {
39127                 Roo.get('loading-mask').hide();
39128             }
39129             
39130             //incomming._node = tnode;
39131             this.form.reset();
39132             //this.dialog.modal = !modal;
39133             //this.dialog.show();
39134             this.el.unmask(); 
39135             
39136             
39137             this.form.setValues({
39138                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
39139                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
39140             });
39141             
39142             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
39143             if (this.form.findField('username').getValue().length > 0 ){
39144                 this.form.findField('password').focus();
39145             } else {
39146                this.form.findField('username').focus();
39147             }
39148     
39149         }
39150     },
39151     items : [
39152          {
39153        
39154             xtype : 'ContentPanel',
39155             xns : Roo,
39156             region: 'center',
39157             fitToFrame : true,
39158             
39159             items : [
39160     
39161                 {
39162                
39163                     xtype : 'Form',
39164                     xns : Roo.form,
39165                     labelWidth: 100,
39166                     style : 'margin: 10px;',
39167                     
39168                     listeners : {
39169                         actionfailed : function(f, act) {
39170                             // form can return { errors: .... }
39171                                 
39172                             //act.result.errors // invalid form element list...
39173                             //act.result.errorMsg// invalid form element list...
39174                             
39175                             this.dialog.el.unmask();
39176                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
39177                                         "Login failed - communication error - try again.");
39178                                       
39179                         },
39180                         actioncomplete: function(re, act) {
39181                              
39182                             Roo.state.Manager.set(
39183                                 this.dialog.realm + '.username',  
39184                                     this.findField('username').getValue()
39185                             );
39186                             Roo.state.Manager.set(
39187                                 this.dialog.realm + '.lang',  
39188                                 this.findField('lang').getValue() 
39189                             );
39190                             
39191                             this.dialog.fillAuth(act.result.data);
39192                               
39193                             this.dialog.hide();
39194                             
39195                             if (Roo.get('loading-mask')) {
39196                                 Roo.get('loading-mask').show();
39197                             }
39198                             Roo.XComponent.build();
39199                             
39200                              
39201                             
39202                         }
39203                     },
39204                     items : [
39205                         {
39206                             xtype : 'TextField',
39207                             xns : Roo.form,
39208                             fieldLabel: "Email Address",
39209                             name: 'username',
39210                             width:200,
39211                             autoCreate : {tag: "input", type: "text", size: "20"}
39212                         },
39213                         {
39214                             xtype : 'TextField',
39215                             xns : Roo.form,
39216                             fieldLabel: "Password",
39217                             inputType: 'password',
39218                             name: 'password',
39219                             width:200,
39220                             autoCreate : {tag: "input", type: "text", size: "20"},
39221                             listeners : {
39222                                 specialkey : function(e,ev) {
39223                                     if (ev.keyCode == 13) {
39224                                         this.form.dialog.el.mask("Logging in");
39225                                         this.form.doAction('submit', {
39226                                             url: this.form.dialog.url,
39227                                             method: this.form.dialog.method
39228                                         });
39229                                     }
39230                                 }
39231                             }  
39232                         },
39233                         {
39234                             xtype : 'ComboBox',
39235                             xns : Roo.form,
39236                             fieldLabel: "Language",
39237                             name : 'langdisp',
39238                             store: {
39239                                 xtype : 'SimpleStore',
39240                                 fields: ['lang', 'ldisp'],
39241                                 data : [
39242                                     [ 'en', 'English' ],
39243                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
39244                                     [ 'zh_CN', '\u7C21\u4E2D' ]
39245                                 ]
39246                             },
39247                             
39248                             valueField : 'lang',
39249                             hiddenName:  'lang',
39250                             width: 200,
39251                             displayField:'ldisp',
39252                             typeAhead: false,
39253                             editable: false,
39254                             mode: 'local',
39255                             triggerAction: 'all',
39256                             emptyText:'Select a Language...',
39257                             selectOnFocus:true,
39258                             listeners : {
39259                                 select :  function(cb, rec, ix) {
39260                                     this.form.switchLang(rec.data.lang);
39261                                 }
39262                             }
39263                         
39264                         }
39265                     ]
39266                 }
39267                   
39268                 
39269             ]
39270         }
39271     ],
39272     buttons : [
39273         {
39274             xtype : 'Button',
39275             xns : 'Roo',
39276             text : "Forgot Password",
39277             listeners : {
39278                 click : function() {
39279                     //console.log(this);
39280                     var n = this.form.findField('username').getValue();
39281                     if (!n.length) {
39282                         Roo.MessageBox.alert("Error", "Fill in your email address");
39283                         return;
39284                     }
39285                     Roo.Ajax.request({
39286                         url: this.dialog.url,
39287                         params: {
39288                             passwordRequest: n
39289                         },
39290                         method: this.dialog.method,
39291                         success:  function(response, opts)  {  // check successfull...
39292                         
39293                             var res = this.dialog.processResponse(response);
39294                             if (!res.success) { // error!
39295                                Roo.MessageBox.alert("Error" ,
39296                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
39297                                return;
39298                             }
39299                             Roo.MessageBox.alert("Notice" ,
39300                                 "Please check you email for the Password Reset message");
39301                         },
39302                         failure : function() {
39303                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
39304                         }
39305                         
39306                     });
39307                 }
39308             }
39309         },
39310         {
39311             xtype : 'Button',
39312             xns : 'Roo',
39313             text : "Login",
39314             listeners : {
39315                 
39316                 click : function () {
39317                         
39318                     this.dialog.el.mask("Logging in");
39319                     this.form.doAction('submit', {
39320                             url: this.dialog.url,
39321                             method: this.dialog.method
39322                     });
39323                 }
39324             }
39325         }
39326     ]
39327   
39328   
39329 })
39330  
39331
39332
39333