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></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.name));
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 : 'className',
26606                 displayField:'val',
26607                 typeAhead: false,
26608                 mode: 'local',
26609                 editable : false,
26610                 triggerAction: 'all',
26611                 emptyText:'Select Style',
26612                 selectOnFocus:true,
26613                 width: 130,
26614                 listeners : {
26615                     'select': function(c, r, i) {
26616                         // initial support only for on class per el..
26617                         tb.selectedNode.className =  r ? r.get('val') : '';
26618                         editor.syncValue();
26619                     }
26620                 }
26621     
26622             }));
26623         }
26624             
26625         
26626         
26627         for (var i in tlist) {
26628             
26629             var item = tlist[i];
26630             tb.add(item.title + ":&nbsp;");
26631             
26632             
26633             
26634             
26635             if (item.opts) {
26636                 // opts == pulldown..
26637                 tb.addField( new Roo.form.ComboBox({
26638                     store: new Roo.data.SimpleStore({
26639                         id : 'val',
26640                         fields: ['val'],
26641                         data : item.opts  
26642                     }),
26643                     name : i,
26644                     displayField:'val',
26645                     typeAhead: false,
26646                     mode: 'local',
26647                     editable : false,
26648                     triggerAction: 'all',
26649                     emptyText:'Select',
26650                     selectOnFocus:true,
26651                     width: item.width ? item.width  : 130,
26652                     listeners : {
26653                         'select': function(c, r, i) {
26654                             tb.selectedNode.setAttribute(c.name, r.get('val'));
26655                         }
26656                     }
26657
26658                 }));
26659                 continue;
26660                     
26661                  
26662                 
26663                 tb.addField( new Roo.form.TextField({
26664                     name: i,
26665                     width: 100,
26666                     //allowBlank:false,
26667                     value: ''
26668                 }));
26669                 continue;
26670             }
26671             tb.addField( new Roo.form.TextField({
26672                 name: i,
26673                 width: item.width,
26674                 //allowBlank:true,
26675                 value: '',
26676                 listeners: {
26677                     'change' : function(f, nv, ov) {
26678                         tb.selectedNode.setAttribute(f.name, nv);
26679                     }
26680                 }
26681             }));
26682              
26683         }
26684         tb.el.on('click', function(e){
26685             e.preventDefault(); // what does this do?
26686         });
26687         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
26688         tb.el.hide();
26689         tb.name = nm;
26690         // dont need to disable them... as they will get hidden
26691         return tb;
26692          
26693         
26694     },
26695     buildFooter : function()
26696     {
26697         
26698         var fel = this.editor.wrap.createChild();
26699         this.footer = new Roo.Toolbar(fel);
26700         // toolbar has scrolly on left / right?
26701         var footDisp= new Roo.Toolbar.Fill();
26702         var _t = this;
26703         this.footer.add(
26704             {
26705                 text : '&lt;',
26706                 xtype: 'Button',
26707                 handler : function() {
26708                     _t.footDisp.scrollTo('left',0,true)
26709                 }
26710             }
26711         );
26712         this.footer.add( footDisp );
26713         this.footer.add( 
26714             {
26715                 text : '&gt;',
26716                 xtype: 'Button',
26717                 handler : function() {
26718                     // no animation..
26719                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
26720                 }
26721             }
26722         );
26723         var fel = Roo.get(footDisp.el);
26724         fel.addClass('x-editor-context');
26725         this.footDispWrap = fel; 
26726         this.footDispWrap.overflow  = 'hidden';
26727         
26728         this.footDisp = fel.createChild();
26729         this.footDispWrap.on('click', this.onContextClick, this)
26730         
26731         
26732     },
26733     onContextClick : function (ev,dom)
26734     {
26735         ev.preventDefault();
26736         var  cn = dom.className;
26737         Roo.log(cn);
26738         if (!cn.match(/x-ed-loc-/)) {
26739             return;
26740         }
26741         var n = cn.split('-').pop();
26742         var ans = this.footerEls;
26743         var sel = ans[n];
26744         
26745          // pick
26746         var range = this.editor.createRange();
26747         
26748         range.selectNodeContents(sel);
26749         //range.selectNode(sel);
26750         
26751         
26752         var selection = this.editor.getSelection();
26753         selection.removeAllRanges();
26754         selection.addRange(range);
26755         
26756         
26757         
26758         this.updateToolbar(null, null, sel);
26759         
26760         
26761     }
26762     
26763     
26764     
26765     
26766     
26767 });
26768
26769
26770
26771
26772
26773 /*
26774  * Based on:
26775  * Ext JS Library 1.1.1
26776  * Copyright(c) 2006-2007, Ext JS, LLC.
26777  *
26778  * Originally Released Under LGPL - original licence link has changed is not relivant.
26779  *
26780  * Fork - LGPL
26781  * <script type="text/javascript">
26782  */
26783  
26784 /**
26785  * @class Roo.form.BasicForm
26786  * @extends Roo.util.Observable
26787  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
26788  * @constructor
26789  * @param {String/HTMLElement/Roo.Element} el The form element or its id
26790  * @param {Object} config Configuration options
26791  */
26792 Roo.form.BasicForm = function(el, config){
26793     this.allItems = [];
26794     this.childForms = [];
26795     Roo.apply(this, config);
26796     /*
26797      * The Roo.form.Field items in this form.
26798      * @type MixedCollection
26799      */
26800      
26801      
26802     this.items = new Roo.util.MixedCollection(false, function(o){
26803         return o.id || (o.id = Roo.id());
26804     });
26805     this.addEvents({
26806         /**
26807          * @event beforeaction
26808          * Fires before any action is performed. Return false to cancel the action.
26809          * @param {Form} this
26810          * @param {Action} action The action to be performed
26811          */
26812         beforeaction: true,
26813         /**
26814          * @event actionfailed
26815          * Fires when an action fails.
26816          * @param {Form} this
26817          * @param {Action} action The action that failed
26818          */
26819         actionfailed : true,
26820         /**
26821          * @event actioncomplete
26822          * Fires when an action is completed.
26823          * @param {Form} this
26824          * @param {Action} action The action that completed
26825          */
26826         actioncomplete : true
26827     });
26828     if(el){
26829         this.initEl(el);
26830     }
26831     Roo.form.BasicForm.superclass.constructor.call(this);
26832 };
26833
26834 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
26835     /**
26836      * @cfg {String} method
26837      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
26838      */
26839     /**
26840      * @cfg {DataReader} reader
26841      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
26842      * This is optional as there is built-in support for processing JSON.
26843      */
26844     /**
26845      * @cfg {DataReader} errorReader
26846      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
26847      * This is completely optional as there is built-in support for processing JSON.
26848      */
26849     /**
26850      * @cfg {String} url
26851      * The URL to use for form actions if one isn't supplied in the action options.
26852      */
26853     /**
26854      * @cfg {Boolean} fileUpload
26855      * Set to true if this form is a file upload.
26856      */
26857      
26858     /**
26859      * @cfg {Object} baseParams
26860      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
26861      */
26862      /**
26863      
26864     /**
26865      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26866      */
26867     timeout: 30,
26868
26869     // private
26870     activeAction : null,
26871
26872     /**
26873      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26874      * or setValues() data instead of when the form was first created.
26875      */
26876     trackResetOnLoad : false,
26877     
26878     
26879     /**
26880      * childForms - used for multi-tab forms
26881      * @type {Array}
26882      */
26883     childForms : false,
26884     
26885     /**
26886      * allItems - full list of fields.
26887      * @type {Array}
26888      */
26889     allItems : false,
26890     
26891     /**
26892      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26893      * element by passing it or its id or mask the form itself by passing in true.
26894      * @type Mixed
26895      */
26896     waitMsgTarget : false,
26897
26898     // private
26899     initEl : function(el){
26900         this.el = Roo.get(el);
26901         this.id = this.el.id || Roo.id();
26902         this.el.on('submit', this.onSubmit, this);
26903         this.el.addClass('x-form');
26904     },
26905
26906     // private
26907     onSubmit : function(e){
26908         e.stopEvent();
26909     },
26910
26911     /**
26912      * Returns true if client-side validation on the form is successful.
26913      * @return Boolean
26914      */
26915     isValid : function(){
26916         var valid = true;
26917         this.items.each(function(f){
26918            if(!f.validate()){
26919                valid = false;
26920            }
26921         });
26922         return valid;
26923     },
26924
26925     /**
26926      * Returns true if any fields in this form have changed since their original load.
26927      * @return Boolean
26928      */
26929     isDirty : function(){
26930         var dirty = false;
26931         this.items.each(function(f){
26932            if(f.isDirty()){
26933                dirty = true;
26934                return false;
26935            }
26936         });
26937         return dirty;
26938     },
26939
26940     /**
26941      * Performs a predefined action (submit or load) or custom actions you define on this form.
26942      * @param {String} actionName The name of the action type
26943      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26944      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26945      * accept other config options):
26946      * <pre>
26947 Property          Type             Description
26948 ----------------  ---------------  ----------------------------------------------------------------------------------
26949 url               String           The url for the action (defaults to the form's url)
26950 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26951 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26952 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26953                                    validate the form on the client (defaults to false)
26954      * </pre>
26955      * @return {BasicForm} this
26956      */
26957     doAction : function(action, options){
26958         if(typeof action == 'string'){
26959             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26960         }
26961         if(this.fireEvent('beforeaction', this, action) !== false){
26962             this.beforeAction(action);
26963             action.run.defer(100, action);
26964         }
26965         return this;
26966     },
26967
26968     /**
26969      * Shortcut to do a submit action.
26970      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26971      * @return {BasicForm} this
26972      */
26973     submit : function(options){
26974         this.doAction('submit', options);
26975         return this;
26976     },
26977
26978     /**
26979      * Shortcut to do a load action.
26980      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26981      * @return {BasicForm} this
26982      */
26983     load : function(options){
26984         this.doAction('load', options);
26985         return this;
26986     },
26987
26988     /**
26989      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26990      * @param {Record} record The record to edit
26991      * @return {BasicForm} this
26992      */
26993     updateRecord : function(record){
26994         record.beginEdit();
26995         var fs = record.fields;
26996         fs.each(function(f){
26997             var field = this.findField(f.name);
26998             if(field){
26999                 record.set(f.name, field.getValue());
27000             }
27001         }, this);
27002         record.endEdit();
27003         return this;
27004     },
27005
27006     /**
27007      * Loads an Roo.data.Record into this form.
27008      * @param {Record} record The record to load
27009      * @return {BasicForm} this
27010      */
27011     loadRecord : function(record){
27012         this.setValues(record.data);
27013         return this;
27014     },
27015
27016     // private
27017     beforeAction : function(action){
27018         var o = action.options;
27019         
27020        
27021         if(this.waitMsgTarget === true){
27022             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
27023         }else if(this.waitMsgTarget){
27024             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
27025             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
27026         }else {
27027             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
27028         }
27029          
27030     },
27031
27032     // private
27033     afterAction : function(action, success){
27034         this.activeAction = null;
27035         var o = action.options;
27036         
27037         if(this.waitMsgTarget === true){
27038             this.el.unmask();
27039         }else if(this.waitMsgTarget){
27040             this.waitMsgTarget.unmask();
27041         }else{
27042             Roo.MessageBox.updateProgress(1);
27043             Roo.MessageBox.hide();
27044         }
27045          
27046         if(success){
27047             if(o.reset){
27048                 this.reset();
27049             }
27050             Roo.callback(o.success, o.scope, [this, action]);
27051             this.fireEvent('actioncomplete', this, action);
27052             
27053         }else{
27054             
27055             // failure condition..
27056             // we have a scenario where updates need confirming.
27057             // eg. if a locking scenario exists..
27058             // we look for { errors : { needs_confirm : true }} in the response.
27059             if (
27060                 (typeof(action.result) != 'undefined')  &&
27061                 (typeof(action.result.errors) != 'undefined')  &&
27062                 (typeof(action.result.errors.needs_confirm) != 'undefined')
27063           ){
27064                 var _t = this;
27065                 Roo.MessageBox.confirm(
27066                     "Change requires confirmation",
27067                     action.result.errorMsg,
27068                     function(r) {
27069                         if (r != 'yes') {
27070                             return;
27071                         }
27072                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
27073                     }
27074                     
27075                 );
27076                 
27077                 
27078                 
27079                 return;
27080             }
27081             
27082             Roo.callback(o.failure, o.scope, [this, action]);
27083             // show an error message if no failed handler is set..
27084             if (!this.hasListener('actionfailed')) {
27085                 Roo.MessageBox.alert("Error",
27086                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
27087                         action.result.errorMsg :
27088                         "Saving Failed, please check your entries or try again"
27089                 );
27090             }
27091             
27092             this.fireEvent('actionfailed', this, action);
27093         }
27094         
27095     },
27096
27097     /**
27098      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
27099      * @param {String} id The value to search for
27100      * @return Field
27101      */
27102     findField : function(id){
27103         var field = this.items.get(id);
27104         if(!field){
27105             this.items.each(function(f){
27106                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
27107                     field = f;
27108                     return false;
27109                 }
27110             });
27111         }
27112         return field || null;
27113     },
27114
27115     /**
27116      * Add a secondary form to this one, 
27117      * Used to provide tabbed forms. One form is primary, with hidden values 
27118      * which mirror the elements from the other forms.
27119      * 
27120      * @param {Roo.form.Form} form to add.
27121      * 
27122      */
27123     addForm : function(form)
27124     {
27125        
27126         if (this.childForms.indexOf(form) > -1) {
27127             // already added..
27128             return;
27129         }
27130         this.childForms.push(form);
27131         var n = '';
27132         Roo.each(form.allItems, function (fe) {
27133             
27134             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
27135             if (this.findField(n)) { // already added..
27136                 return;
27137             }
27138             var add = new Roo.form.Hidden({
27139                 name : n
27140             });
27141             add.render(this.el);
27142             
27143             this.add( add );
27144         }, this);
27145         
27146     },
27147     /**
27148      * Mark fields in this form invalid in bulk.
27149      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
27150      * @return {BasicForm} this
27151      */
27152     markInvalid : function(errors){
27153         if(errors instanceof Array){
27154             for(var i = 0, len = errors.length; i < len; i++){
27155                 var fieldError = errors[i];
27156                 var f = this.findField(fieldError.id);
27157                 if(f){
27158                     f.markInvalid(fieldError.msg);
27159                 }
27160             }
27161         }else{
27162             var field, id;
27163             for(id in errors){
27164                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
27165                     field.markInvalid(errors[id]);
27166                 }
27167             }
27168         }
27169         Roo.each(this.childForms || [], function (f) {
27170             f.markInvalid(errors);
27171         });
27172         
27173         return this;
27174     },
27175
27176     /**
27177      * Set values for fields in this form in bulk.
27178      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
27179      * @return {BasicForm} this
27180      */
27181     setValues : function(values){
27182         if(values instanceof Array){ // array of objects
27183             for(var i = 0, len = values.length; i < len; i++){
27184                 var v = values[i];
27185                 var f = this.findField(v.id);
27186                 if(f){
27187                     f.setValue(v.value);
27188                     if(this.trackResetOnLoad){
27189                         f.originalValue = f.getValue();
27190                     }
27191                 }
27192             }
27193         }else{ // object hash
27194             var field, id;
27195             for(id in values){
27196                 if(typeof values[id] != 'function' && (field = this.findField(id))){
27197                     
27198                     if (field.setFromData && 
27199                         field.valueField && 
27200                         field.displayField &&
27201                         // combos' with local stores can 
27202                         // be queried via setValue()
27203                         // to set their value..
27204                         (field.store && !field.store.isLocal)
27205                         ) {
27206                         // it's a combo
27207                         var sd = { };
27208                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
27209                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
27210                         field.setFromData(sd);
27211                         
27212                     } else {
27213                         field.setValue(values[id]);
27214                     }
27215                     
27216                     
27217                     if(this.trackResetOnLoad){
27218                         field.originalValue = field.getValue();
27219                     }
27220                 }
27221             }
27222         }
27223          
27224         Roo.each(this.childForms || [], function (f) {
27225             f.setValues(values);
27226         });
27227                 
27228         return this;
27229     },
27230
27231     /**
27232      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
27233      * they are returned as an array.
27234      * @param {Boolean} asString
27235      * @return {Object}
27236      */
27237     getValues : function(asString){
27238         if (this.childForms) {
27239             // copy values from the child forms
27240             Roo.each(this.childForms, function (f) {
27241                 this.setValues(f.getValues());
27242             }, this);
27243         }
27244         
27245         
27246         
27247         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
27248         if(asString === true){
27249             return fs;
27250         }
27251         return Roo.urlDecode(fs);
27252     },
27253     
27254     /**
27255      * Returns the fields in this form as an object with key/value pairs. 
27256      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
27257      * @return {Object}
27258      */
27259     getFieldValues : function(with_hidden)
27260     {
27261         if (this.childForms) {
27262             // copy values from the child forms
27263             // should this call getFieldValues - probably not as we do not currently copy
27264             // hidden fields when we generate..
27265             Roo.each(this.childForms, function (f) {
27266                 this.setValues(f.getValues());
27267             }, this);
27268         }
27269         
27270         var ret = {};
27271         this.items.each(function(f){
27272             if (!f.getName()) {
27273                 return;
27274             }
27275             var v = f.getValue();
27276             // not sure if this supported any more..
27277             if ((typeof(v) == 'object') && f.getRawValue) {
27278                 v = f.getRawValue() ; // dates..
27279             }
27280             // combo boxes where name != hiddenName...
27281             if (f.name != f.getName()) {
27282                 ret[f.name] = f.getRawValue();
27283             }
27284             ret[f.getName()] = v;
27285         });
27286         
27287         return ret;
27288     },
27289
27290     /**
27291      * Clears all invalid messages in this form.
27292      * @return {BasicForm} this
27293      */
27294     clearInvalid : function(){
27295         this.items.each(function(f){
27296            f.clearInvalid();
27297         });
27298         
27299         Roo.each(this.childForms || [], function (f) {
27300             f.clearInvalid();
27301         });
27302         
27303         
27304         return this;
27305     },
27306
27307     /**
27308      * Resets this form.
27309      * @return {BasicForm} this
27310      */
27311     reset : function(){
27312         this.items.each(function(f){
27313             f.reset();
27314         });
27315         
27316         Roo.each(this.childForms || [], function (f) {
27317             f.reset();
27318         });
27319        
27320         
27321         return this;
27322     },
27323
27324     /**
27325      * Add Roo.form components to this form.
27326      * @param {Field} field1
27327      * @param {Field} field2 (optional)
27328      * @param {Field} etc (optional)
27329      * @return {BasicForm} this
27330      */
27331     add : function(){
27332         this.items.addAll(Array.prototype.slice.call(arguments, 0));
27333         return this;
27334     },
27335
27336
27337     /**
27338      * Removes a field from the items collection (does NOT remove its markup).
27339      * @param {Field} field
27340      * @return {BasicForm} this
27341      */
27342     remove : function(field){
27343         this.items.remove(field);
27344         return this;
27345     },
27346
27347     /**
27348      * Looks at the fields in this form, checks them for an id attribute,
27349      * and calls applyTo on the existing dom element with that id.
27350      * @return {BasicForm} this
27351      */
27352     render : function(){
27353         this.items.each(function(f){
27354             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
27355                 f.applyTo(f.id);
27356             }
27357         });
27358         return this;
27359     },
27360
27361     /**
27362      * Calls {@link Ext#apply} for all fields in this form with the passed object.
27363      * @param {Object} values
27364      * @return {BasicForm} this
27365      */
27366     applyToFields : function(o){
27367         this.items.each(function(f){
27368            Roo.apply(f, o);
27369         });
27370         return this;
27371     },
27372
27373     /**
27374      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
27375      * @param {Object} values
27376      * @return {BasicForm} this
27377      */
27378     applyIfToFields : function(o){
27379         this.items.each(function(f){
27380            Roo.applyIf(f, o);
27381         });
27382         return this;
27383     }
27384 });
27385
27386 // back compat
27387 Roo.BasicForm = Roo.form.BasicForm;/*
27388  * Based on:
27389  * Ext JS Library 1.1.1
27390  * Copyright(c) 2006-2007, Ext JS, LLC.
27391  *
27392  * Originally Released Under LGPL - original licence link has changed is not relivant.
27393  *
27394  * Fork - LGPL
27395  * <script type="text/javascript">
27396  */
27397
27398 /**
27399  * @class Roo.form.Form
27400  * @extends Roo.form.BasicForm
27401  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
27402  * @constructor
27403  * @param {Object} config Configuration options
27404  */
27405 Roo.form.Form = function(config){
27406     var xitems =  [];
27407     if (config.items) {
27408         xitems = config.items;
27409         delete config.items;
27410     }
27411    
27412     
27413     Roo.form.Form.superclass.constructor.call(this, null, config);
27414     this.url = this.url || this.action;
27415     if(!this.root){
27416         this.root = new Roo.form.Layout(Roo.applyIf({
27417             id: Roo.id()
27418         }, config));
27419     }
27420     this.active = this.root;
27421     /**
27422      * Array of all the buttons that have been added to this form via {@link addButton}
27423      * @type Array
27424      */
27425     this.buttons = [];
27426     this.allItems = [];
27427     this.addEvents({
27428         /**
27429          * @event clientvalidation
27430          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
27431          * @param {Form} this
27432          * @param {Boolean} valid true if the form has passed client-side validation
27433          */
27434         clientvalidation: true,
27435         /**
27436          * @event rendered
27437          * Fires when the form is rendered
27438          * @param {Roo.form.Form} form
27439          */
27440         rendered : true
27441     });
27442     
27443     if (this.progressUrl) {
27444             // push a hidden field onto the list of fields..
27445             this.addxtype( {
27446                     xns: Roo.form, 
27447                     xtype : 'Hidden', 
27448                     name : 'UPLOAD_IDENTIFIER' 
27449             });
27450         }
27451         
27452     
27453     Roo.each(xitems, this.addxtype, this);
27454     
27455     
27456     
27457 };
27458
27459 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
27460     /**
27461      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
27462      */
27463     /**
27464      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
27465      */
27466     /**
27467      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
27468      */
27469     buttonAlign:'center',
27470
27471     /**
27472      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
27473      */
27474     minButtonWidth:75,
27475
27476     /**
27477      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
27478      * This property cascades to child containers if not set.
27479      */
27480     labelAlign:'left',
27481
27482     /**
27483      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
27484      * fires a looping event with that state. This is required to bind buttons to the valid
27485      * state using the config value formBind:true on the button.
27486      */
27487     monitorValid : false,
27488
27489     /**
27490      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
27491      */
27492     monitorPoll : 200,
27493     
27494     /**
27495      * @cfg {String} progressUrl - Url to return progress data 
27496      */
27497     
27498     progressUrl : false,
27499   
27500     /**
27501      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
27502      * fields are added and the column is closed. If no fields are passed the column remains open
27503      * until end() is called.
27504      * @param {Object} config The config to pass to the column
27505      * @param {Field} field1 (optional)
27506      * @param {Field} field2 (optional)
27507      * @param {Field} etc (optional)
27508      * @return Column The column container object
27509      */
27510     column : function(c){
27511         var col = new Roo.form.Column(c);
27512         this.start(col);
27513         if(arguments.length > 1){ // duplicate code required because of Opera
27514             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27515             this.end();
27516         }
27517         return col;
27518     },
27519
27520     /**
27521      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
27522      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
27523      * until end() is called.
27524      * @param {Object} config The config to pass to the fieldset
27525      * @param {Field} field1 (optional)
27526      * @param {Field} field2 (optional)
27527      * @param {Field} etc (optional)
27528      * @return FieldSet The fieldset container object
27529      */
27530     fieldset : function(c){
27531         var fs = new Roo.form.FieldSet(c);
27532         this.start(fs);
27533         if(arguments.length > 1){ // duplicate code required because of Opera
27534             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27535             this.end();
27536         }
27537         return fs;
27538     },
27539
27540     /**
27541      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
27542      * fields are added and the container is closed. If no fields are passed the container remains open
27543      * until end() is called.
27544      * @param {Object} config The config to pass to the Layout
27545      * @param {Field} field1 (optional)
27546      * @param {Field} field2 (optional)
27547      * @param {Field} etc (optional)
27548      * @return Layout The container object
27549      */
27550     container : function(c){
27551         var l = new Roo.form.Layout(c);
27552         this.start(l);
27553         if(arguments.length > 1){ // duplicate code required because of Opera
27554             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27555             this.end();
27556         }
27557         return l;
27558     },
27559
27560     /**
27561      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
27562      * @param {Object} container A Roo.form.Layout or subclass of Layout
27563      * @return {Form} this
27564      */
27565     start : function(c){
27566         // cascade label info
27567         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
27568         this.active.stack.push(c);
27569         c.ownerCt = this.active;
27570         this.active = c;
27571         return this;
27572     },
27573
27574     /**
27575      * Closes the current open container
27576      * @return {Form} this
27577      */
27578     end : function(){
27579         if(this.active == this.root){
27580             return this;
27581         }
27582         this.active = this.active.ownerCt;
27583         return this;
27584     },
27585
27586     /**
27587      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
27588      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
27589      * as the label of the field.
27590      * @param {Field} field1
27591      * @param {Field} field2 (optional)
27592      * @param {Field} etc. (optional)
27593      * @return {Form} this
27594      */
27595     add : function(){
27596         this.active.stack.push.apply(this.active.stack, arguments);
27597         this.allItems.push.apply(this.allItems,arguments);
27598         var r = [];
27599         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
27600             if(a[i].isFormField){
27601                 r.push(a[i]);
27602             }
27603         }
27604         if(r.length > 0){
27605             Roo.form.Form.superclass.add.apply(this, r);
27606         }
27607         return this;
27608     },
27609     
27610
27611     
27612     
27613     
27614      /**
27615      * Find any element that has been added to a form, using it's ID or name
27616      * This can include framesets, columns etc. along with regular fields..
27617      * @param {String} id - id or name to find.
27618      
27619      * @return {Element} e - or false if nothing found.
27620      */
27621     findbyId : function(id)
27622     {
27623         var ret = false;
27624         if (!id) {
27625             return ret;
27626         }
27627         Roo.each(this.allItems, function(f){
27628             if (f.id == id || f.name == id ){
27629                 ret = f;
27630                 return false;
27631             }
27632         });
27633         return ret;
27634     },
27635
27636     
27637     
27638     /**
27639      * Render this form into the passed container. This should only be called once!
27640      * @param {String/HTMLElement/Element} container The element this component should be rendered into
27641      * @return {Form} this
27642      */
27643     render : function(ct)
27644     {
27645         
27646         
27647         
27648         ct = Roo.get(ct);
27649         var o = this.autoCreate || {
27650             tag: 'form',
27651             method : this.method || 'POST',
27652             id : this.id || Roo.id()
27653         };
27654         this.initEl(ct.createChild(o));
27655
27656         this.root.render(this.el);
27657         
27658        
27659              
27660         this.items.each(function(f){
27661             f.render('x-form-el-'+f.id);
27662         });
27663
27664         if(this.buttons.length > 0){
27665             // tables are required to maintain order and for correct IE layout
27666             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
27667                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
27668                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
27669             }}, null, true);
27670             var tr = tb.getElementsByTagName('tr')[0];
27671             for(var i = 0, len = this.buttons.length; i < len; i++) {
27672                 var b = this.buttons[i];
27673                 var td = document.createElement('td');
27674                 td.className = 'x-form-btn-td';
27675                 b.render(tr.appendChild(td));
27676             }
27677         }
27678         if(this.monitorValid){ // initialize after render
27679             this.startMonitoring();
27680         }
27681         this.fireEvent('rendered', this);
27682         return this;
27683     },
27684
27685     /**
27686      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
27687      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
27688      * object or a valid Roo.DomHelper element config
27689      * @param {Function} handler The function called when the button is clicked
27690      * @param {Object} scope (optional) The scope of the handler function
27691      * @return {Roo.Button}
27692      */
27693     addButton : function(config, handler, scope){
27694         var bc = {
27695             handler: handler,
27696             scope: scope,
27697             minWidth: this.minButtonWidth,
27698             hideParent:true
27699         };
27700         if(typeof config == "string"){
27701             bc.text = config;
27702         }else{
27703             Roo.apply(bc, config);
27704         }
27705         var btn = new Roo.Button(null, bc);
27706         this.buttons.push(btn);
27707         return btn;
27708     },
27709
27710      /**
27711      * Adds a series of form elements (using the xtype property as the factory method.
27712      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
27713      * @param {Object} config 
27714      */
27715     
27716     addxtype : function()
27717     {
27718         var ar = Array.prototype.slice.call(arguments, 0);
27719         var ret = false;
27720         for(var i = 0; i < ar.length; i++) {
27721             if (!ar[i]) {
27722                 continue; // skip -- if this happends something invalid got sent, we 
27723                 // should ignore it, as basically that interface element will not show up
27724                 // and that should be pretty obvious!!
27725             }
27726             
27727             if (Roo.form[ar[i].xtype]) {
27728                 ar[i].form = this;
27729                 var fe = Roo.factory(ar[i], Roo.form);
27730                 if (!ret) {
27731                     ret = fe;
27732                 }
27733                 fe.form = this;
27734                 if (fe.store) {
27735                     fe.store.form = this;
27736                 }
27737                 if (fe.isLayout) {  
27738                          
27739                     this.start(fe);
27740                     this.allItems.push(fe);
27741                     if (fe.items && fe.addxtype) {
27742                         fe.addxtype.apply(fe, fe.items);
27743                         delete fe.items;
27744                     }
27745                      this.end();
27746                     continue;
27747                 }
27748                 
27749                 
27750                  
27751                 this.add(fe);
27752               //  console.log('adding ' + ar[i].xtype);
27753             }
27754             if (ar[i].xtype == 'Button') {  
27755                 //console.log('adding button');
27756                 //console.log(ar[i]);
27757                 this.addButton(ar[i]);
27758                 this.allItems.push(fe);
27759                 continue;
27760             }
27761             
27762             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
27763                 alert('end is not supported on xtype any more, use items');
27764             //    this.end();
27765             //    //console.log('adding end');
27766             }
27767             
27768         }
27769         return ret;
27770     },
27771     
27772     /**
27773      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
27774      * option "monitorValid"
27775      */
27776     startMonitoring : function(){
27777         if(!this.bound){
27778             this.bound = true;
27779             Roo.TaskMgr.start({
27780                 run : this.bindHandler,
27781                 interval : this.monitorPoll || 200,
27782                 scope: this
27783             });
27784         }
27785     },
27786
27787     /**
27788      * Stops monitoring of the valid state of this form
27789      */
27790     stopMonitoring : function(){
27791         this.bound = false;
27792     },
27793
27794     // private
27795     bindHandler : function(){
27796         if(!this.bound){
27797             return false; // stops binding
27798         }
27799         var valid = true;
27800         this.items.each(function(f){
27801             if(!f.isValid(true)){
27802                 valid = false;
27803                 return false;
27804             }
27805         });
27806         for(var i = 0, len = this.buttons.length; i < len; i++){
27807             var btn = this.buttons[i];
27808             if(btn.formBind === true && btn.disabled === valid){
27809                 btn.setDisabled(!valid);
27810             }
27811         }
27812         this.fireEvent('clientvalidation', this, valid);
27813     }
27814     
27815     
27816     
27817     
27818     
27819     
27820     
27821     
27822 });
27823
27824
27825 // back compat
27826 Roo.Form = Roo.form.Form;
27827 /*
27828  * Based on:
27829  * Ext JS Library 1.1.1
27830  * Copyright(c) 2006-2007, Ext JS, LLC.
27831  *
27832  * Originally Released Under LGPL - original licence link has changed is not relivant.
27833  *
27834  * Fork - LGPL
27835  * <script type="text/javascript">
27836  */
27837  
27838  /**
27839  * @class Roo.form.Action
27840  * Internal Class used to handle form actions
27841  * @constructor
27842  * @param {Roo.form.BasicForm} el The form element or its id
27843  * @param {Object} config Configuration options
27844  */
27845  
27846  
27847 // define the action interface
27848 Roo.form.Action = function(form, options){
27849     this.form = form;
27850     this.options = options || {};
27851 };
27852 /**
27853  * Client Validation Failed
27854  * @const 
27855  */
27856 Roo.form.Action.CLIENT_INVALID = 'client';
27857 /**
27858  * Server Validation Failed
27859  * @const 
27860  */
27861  Roo.form.Action.SERVER_INVALID = 'server';
27862  /**
27863  * Connect to Server Failed
27864  * @const 
27865  */
27866 Roo.form.Action.CONNECT_FAILURE = 'connect';
27867 /**
27868  * Reading Data from Server Failed
27869  * @const 
27870  */
27871 Roo.form.Action.LOAD_FAILURE = 'load';
27872
27873 Roo.form.Action.prototype = {
27874     type : 'default',
27875     failureType : undefined,
27876     response : undefined,
27877     result : undefined,
27878
27879     // interface method
27880     run : function(options){
27881
27882     },
27883
27884     // interface method
27885     success : function(response){
27886
27887     },
27888
27889     // interface method
27890     handleResponse : function(response){
27891
27892     },
27893
27894     // default connection failure
27895     failure : function(response){
27896         
27897         this.response = response;
27898         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27899         this.form.afterAction(this, false);
27900     },
27901
27902     processResponse : function(response){
27903         this.response = response;
27904         if(!response.responseText){
27905             return true;
27906         }
27907         this.result = this.handleResponse(response);
27908         return this.result;
27909     },
27910
27911     // utility functions used internally
27912     getUrl : function(appendParams){
27913         var url = this.options.url || this.form.url || this.form.el.dom.action;
27914         if(appendParams){
27915             var p = this.getParams();
27916             if(p){
27917                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27918             }
27919         }
27920         return url;
27921     },
27922
27923     getMethod : function(){
27924         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27925     },
27926
27927     getParams : function(){
27928         var bp = this.form.baseParams;
27929         var p = this.options.params;
27930         if(p){
27931             if(typeof p == "object"){
27932                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27933             }else if(typeof p == 'string' && bp){
27934                 p += '&' + Roo.urlEncode(bp);
27935             }
27936         }else if(bp){
27937             p = Roo.urlEncode(bp);
27938         }
27939         return p;
27940     },
27941
27942     createCallback : function(){
27943         return {
27944             success: this.success,
27945             failure: this.failure,
27946             scope: this,
27947             timeout: (this.form.timeout*1000),
27948             upload: this.form.fileUpload ? this.success : undefined
27949         };
27950     }
27951 };
27952
27953 Roo.form.Action.Submit = function(form, options){
27954     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27955 };
27956
27957 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27958     type : 'submit',
27959
27960     haveProgress : false,
27961     uploadComplete : false,
27962     
27963     // uploadProgress indicator.
27964     uploadProgress : function()
27965     {
27966         if (!this.form.progressUrl) {
27967             return;
27968         }
27969         
27970         if (!this.haveProgress) {
27971             Roo.MessageBox.progress("Uploading", "Uploading");
27972         }
27973         if (this.uploadComplete) {
27974            Roo.MessageBox.hide();
27975            return;
27976         }
27977         
27978         this.haveProgress = true;
27979    
27980         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27981         
27982         var c = new Roo.data.Connection();
27983         c.request({
27984             url : this.form.progressUrl,
27985             params: {
27986                 id : uid
27987             },
27988             method: 'GET',
27989             success : function(req){
27990                //console.log(data);
27991                 var rdata = false;
27992                 var edata;
27993                 try  {
27994                    rdata = Roo.decode(req.responseText)
27995                 } catch (e) {
27996                     Roo.log("Invalid data from server..");
27997                     Roo.log(edata);
27998                     return;
27999                 }
28000                 if (!rdata || !rdata.success) {
28001                     Roo.log(rdata);
28002                     return;
28003                 }
28004                 var data = rdata.data;
28005                 
28006                 if (this.uploadComplete) {
28007                    Roo.MessageBox.hide();
28008                    return;
28009                 }
28010                    
28011                 if (data){
28012                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
28013                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
28014                     );
28015                 }
28016                 this.uploadProgress.defer(2000,this);
28017             },
28018        
28019             failure: function(data) {
28020                 Roo.log('progress url failed ');
28021                 Roo.log(data);
28022             },
28023             scope : this
28024         });
28025            
28026     },
28027     
28028     
28029     run : function()
28030     {
28031         // run get Values on the form, so it syncs any secondary forms.
28032         this.form.getValues();
28033         
28034         var o = this.options;
28035         var method = this.getMethod();
28036         var isPost = method == 'POST';
28037         if(o.clientValidation === false || this.form.isValid()){
28038             
28039             if (this.form.progressUrl) {
28040                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
28041                     (new Date() * 1) + '' + Math.random());
28042                     
28043             } 
28044             
28045             
28046             Roo.Ajax.request(Roo.apply(this.createCallback(), {
28047                 form:this.form.el.dom,
28048                 url:this.getUrl(!isPost),
28049                 method: method,
28050                 params:isPost ? this.getParams() : null,
28051                 isUpload: this.form.fileUpload
28052             }));
28053             
28054             this.uploadProgress();
28055
28056         }else if (o.clientValidation !== false){ // client validation failed
28057             this.failureType = Roo.form.Action.CLIENT_INVALID;
28058             this.form.afterAction(this, false);
28059         }
28060     },
28061
28062     success : function(response)
28063     {
28064         this.uploadComplete= true;
28065         if (this.haveProgress) {
28066             Roo.MessageBox.hide();
28067         }
28068         
28069         
28070         var result = this.processResponse(response);
28071         if(result === true || result.success){
28072             this.form.afterAction(this, true);
28073             return;
28074         }
28075         if(result.errors){
28076             this.form.markInvalid(result.errors);
28077             this.failureType = Roo.form.Action.SERVER_INVALID;
28078         }
28079         this.form.afterAction(this, false);
28080     },
28081     failure : function(response)
28082     {
28083         this.uploadComplete= true;
28084         if (this.haveProgress) {
28085             Roo.MessageBox.hide();
28086         }
28087         
28088         this.response = response;
28089         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28090         this.form.afterAction(this, false);
28091     },
28092     
28093     handleResponse : function(response){
28094         if(this.form.errorReader){
28095             var rs = this.form.errorReader.read(response);
28096             var errors = [];
28097             if(rs.records){
28098                 for(var i = 0, len = rs.records.length; i < len; i++) {
28099                     var r = rs.records[i];
28100                     errors[i] = r.data;
28101                 }
28102             }
28103             if(errors.length < 1){
28104                 errors = null;
28105             }
28106             return {
28107                 success : rs.success,
28108                 errors : errors
28109             };
28110         }
28111         var ret = false;
28112         try {
28113             ret = Roo.decode(response.responseText);
28114         } catch (e) {
28115             ret = {
28116                 success: false,
28117                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
28118                 errors : []
28119             };
28120         }
28121         return ret;
28122         
28123     }
28124 });
28125
28126
28127 Roo.form.Action.Load = function(form, options){
28128     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
28129     this.reader = this.form.reader;
28130 };
28131
28132 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
28133     type : 'load',
28134
28135     run : function(){
28136         
28137         Roo.Ajax.request(Roo.apply(
28138                 this.createCallback(), {
28139                     method:this.getMethod(),
28140                     url:this.getUrl(false),
28141                     params:this.getParams()
28142         }));
28143     },
28144
28145     success : function(response){
28146         
28147         var result = this.processResponse(response);
28148         if(result === true || !result.success || !result.data){
28149             this.failureType = Roo.form.Action.LOAD_FAILURE;
28150             this.form.afterAction(this, false);
28151             return;
28152         }
28153         this.form.clearInvalid();
28154         this.form.setValues(result.data);
28155         this.form.afterAction(this, true);
28156     },
28157
28158     handleResponse : function(response){
28159         if(this.form.reader){
28160             var rs = this.form.reader.read(response);
28161             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
28162             return {
28163                 success : rs.success,
28164                 data : data
28165             };
28166         }
28167         return Roo.decode(response.responseText);
28168     }
28169 });
28170
28171 Roo.form.Action.ACTION_TYPES = {
28172     'load' : Roo.form.Action.Load,
28173     'submit' : Roo.form.Action.Submit
28174 };/*
28175  * Based on:
28176  * Ext JS Library 1.1.1
28177  * Copyright(c) 2006-2007, Ext JS, LLC.
28178  *
28179  * Originally Released Under LGPL - original licence link has changed is not relivant.
28180  *
28181  * Fork - LGPL
28182  * <script type="text/javascript">
28183  */
28184  
28185 /**
28186  * @class Roo.form.Layout
28187  * @extends Roo.Component
28188  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
28189  * @constructor
28190  * @param {Object} config Configuration options
28191  */
28192 Roo.form.Layout = function(config){
28193     var xitems = [];
28194     if (config.items) {
28195         xitems = config.items;
28196         delete config.items;
28197     }
28198     Roo.form.Layout.superclass.constructor.call(this, config);
28199     this.stack = [];
28200     Roo.each(xitems, this.addxtype, this);
28201      
28202 };
28203
28204 Roo.extend(Roo.form.Layout, Roo.Component, {
28205     /**
28206      * @cfg {String/Object} autoCreate
28207      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
28208      */
28209     /**
28210      * @cfg {String/Object/Function} style
28211      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
28212      * a function which returns such a specification.
28213      */
28214     /**
28215      * @cfg {String} labelAlign
28216      * Valid values are "left," "top" and "right" (defaults to "left")
28217      */
28218     /**
28219      * @cfg {Number} labelWidth
28220      * Fixed width in pixels of all field labels (defaults to undefined)
28221      */
28222     /**
28223      * @cfg {Boolean} clear
28224      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
28225      */
28226     clear : true,
28227     /**
28228      * @cfg {String} labelSeparator
28229      * The separator to use after field labels (defaults to ':')
28230      */
28231     labelSeparator : ':',
28232     /**
28233      * @cfg {Boolean} hideLabels
28234      * True to suppress the display of field labels in this layout (defaults to false)
28235      */
28236     hideLabels : false,
28237
28238     // private
28239     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
28240     
28241     isLayout : true,
28242     
28243     // private
28244     onRender : function(ct, position){
28245         if(this.el){ // from markup
28246             this.el = Roo.get(this.el);
28247         }else {  // generate
28248             var cfg = this.getAutoCreate();
28249             this.el = ct.createChild(cfg, position);
28250         }
28251         if(this.style){
28252             this.el.applyStyles(this.style);
28253         }
28254         if(this.labelAlign){
28255             this.el.addClass('x-form-label-'+this.labelAlign);
28256         }
28257         if(this.hideLabels){
28258             this.labelStyle = "display:none";
28259             this.elementStyle = "padding-left:0;";
28260         }else{
28261             if(typeof this.labelWidth == 'number'){
28262                 this.labelStyle = "width:"+this.labelWidth+"px;";
28263                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
28264             }
28265             if(this.labelAlign == 'top'){
28266                 this.labelStyle = "width:auto;";
28267                 this.elementStyle = "padding-left:0;";
28268             }
28269         }
28270         var stack = this.stack;
28271         var slen = stack.length;
28272         if(slen > 0){
28273             if(!this.fieldTpl){
28274                 var t = new Roo.Template(
28275                     '<div class="x-form-item {5}">',
28276                         '<label for="{0}" style="{2}">{1}{4}</label>',
28277                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28278                         '</div>',
28279                     '</div><div class="x-form-clear-left"></div>'
28280                 );
28281                 t.disableFormats = true;
28282                 t.compile();
28283                 Roo.form.Layout.prototype.fieldTpl = t;
28284             }
28285             for(var i = 0; i < slen; i++) {
28286                 if(stack[i].isFormField){
28287                     this.renderField(stack[i]);
28288                 }else{
28289                     this.renderComponent(stack[i]);
28290                 }
28291             }
28292         }
28293         if(this.clear){
28294             this.el.createChild({cls:'x-form-clear'});
28295         }
28296     },
28297
28298     // private
28299     renderField : function(f){
28300         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
28301                f.id, //0
28302                f.fieldLabel, //1
28303                f.labelStyle||this.labelStyle||'', //2
28304                this.elementStyle||'', //3
28305                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
28306                f.itemCls||this.itemCls||''  //5
28307        ], true).getPrevSibling());
28308     },
28309
28310     // private
28311     renderComponent : function(c){
28312         c.render(c.isLayout ? this.el : this.el.createChild());    
28313     },
28314     /**
28315      * Adds a object form elements (using the xtype property as the factory method.)
28316      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
28317      * @param {Object} config 
28318      */
28319     addxtype : function(o)
28320     {
28321         // create the lement.
28322         o.form = this.form;
28323         var fe = Roo.factory(o, Roo.form);
28324         this.form.allItems.push(fe);
28325         this.stack.push(fe);
28326         
28327         if (fe.isFormField) {
28328             this.form.items.add(fe);
28329         }
28330          
28331         return fe;
28332     }
28333 });
28334
28335 /**
28336  * @class Roo.form.Column
28337  * @extends Roo.form.Layout
28338  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
28339  * @constructor
28340  * @param {Object} config Configuration options
28341  */
28342 Roo.form.Column = function(config){
28343     Roo.form.Column.superclass.constructor.call(this, config);
28344 };
28345
28346 Roo.extend(Roo.form.Column, Roo.form.Layout, {
28347     /**
28348      * @cfg {Number/String} width
28349      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28350      */
28351     /**
28352      * @cfg {String/Object} autoCreate
28353      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
28354      */
28355
28356     // private
28357     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
28358
28359     // private
28360     onRender : function(ct, position){
28361         Roo.form.Column.superclass.onRender.call(this, ct, position);
28362         if(this.width){
28363             this.el.setWidth(this.width);
28364         }
28365     }
28366 });
28367
28368
28369 /**
28370  * @class Roo.form.Row
28371  * @extends Roo.form.Layout
28372  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
28373  * @constructor
28374  * @param {Object} config Configuration options
28375  */
28376
28377  
28378 Roo.form.Row = function(config){
28379     Roo.form.Row.superclass.constructor.call(this, config);
28380 };
28381  
28382 Roo.extend(Roo.form.Row, Roo.form.Layout, {
28383       /**
28384      * @cfg {Number/String} width
28385      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28386      */
28387     /**
28388      * @cfg {Number/String} height
28389      * The fixed height of the column in pixels or CSS value (defaults to "auto")
28390      */
28391     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
28392     
28393     padWidth : 20,
28394     // private
28395     onRender : function(ct, position){
28396         //console.log('row render');
28397         if(!this.rowTpl){
28398             var t = new Roo.Template(
28399                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
28400                     '<label for="{0}" style="{2}">{1}{4}</label>',
28401                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28402                     '</div>',
28403                 '</div>'
28404             );
28405             t.disableFormats = true;
28406             t.compile();
28407             Roo.form.Layout.prototype.rowTpl = t;
28408         }
28409         this.fieldTpl = this.rowTpl;
28410         
28411         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
28412         var labelWidth = 100;
28413         
28414         if ((this.labelAlign != 'top')) {
28415             if (typeof this.labelWidth == 'number') {
28416                 labelWidth = this.labelWidth
28417             }
28418             this.padWidth =  20 + labelWidth;
28419             
28420         }
28421         
28422         Roo.form.Column.superclass.onRender.call(this, ct, position);
28423         if(this.width){
28424             this.el.setWidth(this.width);
28425         }
28426         if(this.height){
28427             this.el.setHeight(this.height);
28428         }
28429     },
28430     
28431     // private
28432     renderField : function(f){
28433         f.fieldEl = this.fieldTpl.append(this.el, [
28434                f.id, f.fieldLabel,
28435                f.labelStyle||this.labelStyle||'',
28436                this.elementStyle||'',
28437                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
28438                f.itemCls||this.itemCls||'',
28439                f.width ? f.width + this.padWidth : 160 + this.padWidth
28440        ],true);
28441     }
28442 });
28443  
28444
28445 /**
28446  * @class Roo.form.FieldSet
28447  * @extends Roo.form.Layout
28448  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
28449  * @constructor
28450  * @param {Object} config Configuration options
28451  */
28452 Roo.form.FieldSet = function(config){
28453     Roo.form.FieldSet.superclass.constructor.call(this, config);
28454 };
28455
28456 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
28457     /**
28458      * @cfg {String} legend
28459      * The text to display as the legend for the FieldSet (defaults to '')
28460      */
28461     /**
28462      * @cfg {String/Object} autoCreate
28463      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
28464      */
28465
28466     // private
28467     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
28468
28469     // private
28470     onRender : function(ct, position){
28471         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
28472         if(this.legend){
28473             this.setLegend(this.legend);
28474         }
28475     },
28476
28477     // private
28478     setLegend : function(text){
28479         if(this.rendered){
28480             this.el.child('legend').update(text);
28481         }
28482     }
28483 });/*
28484  * Based on:
28485  * Ext JS Library 1.1.1
28486  * Copyright(c) 2006-2007, Ext JS, LLC.
28487  *
28488  * Originally Released Under LGPL - original licence link has changed is not relivant.
28489  *
28490  * Fork - LGPL
28491  * <script type="text/javascript">
28492  */
28493 /**
28494  * @class Roo.form.VTypes
28495  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
28496  * @singleton
28497  */
28498 Roo.form.VTypes = function(){
28499     // closure these in so they are only created once.
28500     var alpha = /^[a-zA-Z_]+$/;
28501     var alphanum = /^[a-zA-Z0-9_]+$/;
28502     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
28503     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
28504
28505     // All these messages and functions are configurable
28506     return {
28507         /**
28508          * The function used to validate email addresses
28509          * @param {String} value The email address
28510          */
28511         'email' : function(v){
28512             return email.test(v);
28513         },
28514         /**
28515          * The error text to display when the email validation function returns false
28516          * @type String
28517          */
28518         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
28519         /**
28520          * The keystroke filter mask to be applied on email input
28521          * @type RegExp
28522          */
28523         'emailMask' : /[a-z0-9_\.\-@]/i,
28524
28525         /**
28526          * The function used to validate URLs
28527          * @param {String} value The URL
28528          */
28529         'url' : function(v){
28530             return url.test(v);
28531         },
28532         /**
28533          * The error text to display when the url validation function returns false
28534          * @type String
28535          */
28536         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
28537         
28538         /**
28539          * The function used to validate alpha values
28540          * @param {String} value The value
28541          */
28542         'alpha' : function(v){
28543             return alpha.test(v);
28544         },
28545         /**
28546          * The error text to display when the alpha validation function returns false
28547          * @type String
28548          */
28549         'alphaText' : 'This field should only contain letters and _',
28550         /**
28551          * The keystroke filter mask to be applied on alpha input
28552          * @type RegExp
28553          */
28554         'alphaMask' : /[a-z_]/i,
28555
28556         /**
28557          * The function used to validate alphanumeric values
28558          * @param {String} value The value
28559          */
28560         'alphanum' : function(v){
28561             return alphanum.test(v);
28562         },
28563         /**
28564          * The error text to display when the alphanumeric validation function returns false
28565          * @type String
28566          */
28567         'alphanumText' : 'This field should only contain letters, numbers and _',
28568         /**
28569          * The keystroke filter mask to be applied on alphanumeric input
28570          * @type RegExp
28571          */
28572         'alphanumMask' : /[a-z0-9_]/i
28573     };
28574 }();//<script type="text/javascript">
28575
28576 /**
28577  * @class Roo.form.FCKeditor
28578  * @extends Roo.form.TextArea
28579  * Wrapper around the FCKEditor http://www.fckeditor.net
28580  * @constructor
28581  * Creates a new FCKeditor
28582  * @param {Object} config Configuration options
28583  */
28584 Roo.form.FCKeditor = function(config){
28585     Roo.form.FCKeditor.superclass.constructor.call(this, config);
28586     this.addEvents({
28587          /**
28588          * @event editorinit
28589          * Fired when the editor is initialized - you can add extra handlers here..
28590          * @param {FCKeditor} this
28591          * @param {Object} the FCK object.
28592          */
28593         editorinit : true
28594     });
28595     
28596     
28597 };
28598 Roo.form.FCKeditor.editors = { };
28599 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
28600 {
28601     //defaultAutoCreate : {
28602     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
28603     //},
28604     // private
28605     /**
28606      * @cfg {Object} fck options - see fck manual for details.
28607      */
28608     fckconfig : false,
28609     
28610     /**
28611      * @cfg {Object} fck toolbar set (Basic or Default)
28612      */
28613     toolbarSet : 'Basic',
28614     /**
28615      * @cfg {Object} fck BasePath
28616      */ 
28617     basePath : '/fckeditor/',
28618     
28619     
28620     frame : false,
28621     
28622     value : '',
28623     
28624    
28625     onRender : function(ct, position)
28626     {
28627         if(!this.el){
28628             this.defaultAutoCreate = {
28629                 tag: "textarea",
28630                 style:"width:300px;height:60px;",
28631                 autocomplete: "off"
28632             };
28633         }
28634         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
28635         /*
28636         if(this.grow){
28637             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
28638             if(this.preventScrollbars){
28639                 this.el.setStyle("overflow", "hidden");
28640             }
28641             this.el.setHeight(this.growMin);
28642         }
28643         */
28644         //console.log('onrender' + this.getId() );
28645         Roo.form.FCKeditor.editors[this.getId()] = this;
28646          
28647
28648         this.replaceTextarea() ;
28649         
28650     },
28651     
28652     getEditor : function() {
28653         return this.fckEditor;
28654     },
28655     /**
28656      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
28657      * @param {Mixed} value The value to set
28658      */
28659     
28660     
28661     setValue : function(value)
28662     {
28663         //console.log('setValue: ' + value);
28664         
28665         if(typeof(value) == 'undefined') { // not sure why this is happending...
28666             return;
28667         }
28668         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28669         
28670         //if(!this.el || !this.getEditor()) {
28671         //    this.value = value;
28672             //this.setValue.defer(100,this,[value]);    
28673         //    return;
28674         //} 
28675         
28676         if(!this.getEditor()) {
28677             return;
28678         }
28679         
28680         this.getEditor().SetData(value);
28681         
28682         //
28683
28684     },
28685
28686     /**
28687      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
28688      * @return {Mixed} value The field value
28689      */
28690     getValue : function()
28691     {
28692         
28693         if (this.frame && this.frame.dom.style.display == 'none') {
28694             return Roo.form.FCKeditor.superclass.getValue.call(this);
28695         }
28696         
28697         if(!this.el || !this.getEditor()) {
28698            
28699            // this.getValue.defer(100,this); 
28700             return this.value;
28701         }
28702        
28703         
28704         var value=this.getEditor().GetData();
28705         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28706         return Roo.form.FCKeditor.superclass.getValue.call(this);
28707         
28708
28709     },
28710
28711     /**
28712      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
28713      * @return {Mixed} value The field value
28714      */
28715     getRawValue : function()
28716     {
28717         if (this.frame && this.frame.dom.style.display == 'none') {
28718             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28719         }
28720         
28721         if(!this.el || !this.getEditor()) {
28722             //this.getRawValue.defer(100,this); 
28723             return this.value;
28724             return;
28725         }
28726         
28727         
28728         
28729         var value=this.getEditor().GetData();
28730         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
28731         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28732          
28733     },
28734     
28735     setSize : function(w,h) {
28736         
28737         
28738         
28739         //if (this.frame && this.frame.dom.style.display == 'none') {
28740         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28741         //    return;
28742         //}
28743         //if(!this.el || !this.getEditor()) {
28744         //    this.setSize.defer(100,this, [w,h]); 
28745         //    return;
28746         //}
28747         
28748         
28749         
28750         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28751         
28752         this.frame.dom.setAttribute('width', w);
28753         this.frame.dom.setAttribute('height', h);
28754         this.frame.setSize(w,h);
28755         
28756     },
28757     
28758     toggleSourceEdit : function(value) {
28759         
28760       
28761          
28762         this.el.dom.style.display = value ? '' : 'none';
28763         this.frame.dom.style.display = value ?  'none' : '';
28764         
28765     },
28766     
28767     
28768     focus: function(tag)
28769     {
28770         if (this.frame.dom.style.display == 'none') {
28771             return Roo.form.FCKeditor.superclass.focus.call(this);
28772         }
28773         if(!this.el || !this.getEditor()) {
28774             this.focus.defer(100,this, [tag]); 
28775             return;
28776         }
28777         
28778         
28779         
28780         
28781         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
28782         this.getEditor().Focus();
28783         if (tgs.length) {
28784             if (!this.getEditor().Selection.GetSelection()) {
28785                 this.focus.defer(100,this, [tag]); 
28786                 return;
28787             }
28788             
28789             
28790             var r = this.getEditor().EditorDocument.createRange();
28791             r.setStart(tgs[0],0);
28792             r.setEnd(tgs[0],0);
28793             this.getEditor().Selection.GetSelection().removeAllRanges();
28794             this.getEditor().Selection.GetSelection().addRange(r);
28795             this.getEditor().Focus();
28796         }
28797         
28798     },
28799     
28800     
28801     
28802     replaceTextarea : function()
28803     {
28804         if ( document.getElementById( this.getId() + '___Frame' ) )
28805             return ;
28806         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
28807         //{
28808             // We must check the elements firstly using the Id and then the name.
28809         var oTextarea = document.getElementById( this.getId() );
28810         
28811         var colElementsByName = document.getElementsByName( this.getId() ) ;
28812          
28813         oTextarea.style.display = 'none' ;
28814
28815         if ( oTextarea.tabIndex ) {            
28816             this.TabIndex = oTextarea.tabIndex ;
28817         }
28818         
28819         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
28820         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
28821         this.frame = Roo.get(this.getId() + '___Frame')
28822     },
28823     
28824     _getConfigHtml : function()
28825     {
28826         var sConfig = '' ;
28827
28828         for ( var o in this.fckconfig ) {
28829             sConfig += sConfig.length > 0  ? '&amp;' : '';
28830             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
28831         }
28832
28833         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
28834     },
28835     
28836     
28837     _getIFrameHtml : function()
28838     {
28839         var sFile = 'fckeditor.html' ;
28840         /* no idea what this is about..
28841         try
28842         {
28843             if ( (/fcksource=true/i).test( window.top.location.search ) )
28844                 sFile = 'fckeditor.original.html' ;
28845         }
28846         catch (e) { 
28847         */
28848
28849         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
28850         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
28851         
28852         
28853         var html = '<iframe id="' + this.getId() +
28854             '___Frame" src="' + sLink +
28855             '" width="' + this.width +
28856             '" height="' + this.height + '"' +
28857             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
28858             ' frameborder="0" scrolling="no"></iframe>' ;
28859
28860         return html ;
28861     },
28862     
28863     _insertHtmlBefore : function( html, element )
28864     {
28865         if ( element.insertAdjacentHTML )       {
28866             // IE
28867             element.insertAdjacentHTML( 'beforeBegin', html ) ;
28868         } else { // Gecko
28869             var oRange = document.createRange() ;
28870             oRange.setStartBefore( element ) ;
28871             var oFragment = oRange.createContextualFragment( html );
28872             element.parentNode.insertBefore( oFragment, element ) ;
28873         }
28874     }
28875     
28876     
28877   
28878     
28879     
28880     
28881     
28882
28883 });
28884
28885 //Roo.reg('fckeditor', Roo.form.FCKeditor);
28886
28887 function FCKeditor_OnComplete(editorInstance){
28888     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
28889     f.fckEditor = editorInstance;
28890     //console.log("loaded");
28891     f.fireEvent('editorinit', f, editorInstance);
28892
28893   
28894
28895  
28896
28897
28898
28899
28900
28901
28902
28903
28904
28905
28906
28907
28908
28909
28910
28911 //<script type="text/javascript">
28912 /**
28913  * @class Roo.form.GridField
28914  * @extends Roo.form.Field
28915  * Embed a grid (or editable grid into a form)
28916  * STATUS ALPHA
28917  * 
28918  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28919  * it needs 
28920  * xgrid.store = Roo.data.Store
28921  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28922  * xgrid.store.reader = Roo.data.JsonReader 
28923  * 
28924  * 
28925  * @constructor
28926  * Creates a new GridField
28927  * @param {Object} config Configuration options
28928  */
28929 Roo.form.GridField = function(config){
28930     Roo.form.GridField.superclass.constructor.call(this, config);
28931      
28932 };
28933
28934 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28935     /**
28936      * @cfg {Number} width  - used to restrict width of grid..
28937      */
28938     width : 100,
28939     /**
28940      * @cfg {Number} height - used to restrict height of grid..
28941      */
28942     height : 50,
28943      /**
28944      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28945          * 
28946          *}
28947      */
28948     xgrid : false, 
28949     /**
28950      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28951      * {tag: "input", type: "checkbox", autocomplete: "off"})
28952      */
28953    // defaultAutoCreate : { tag: 'div' },
28954     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28955     /**
28956      * @cfg {String} addTitle Text to include for adding a title.
28957      */
28958     addTitle : false,
28959     //
28960     onResize : function(){
28961         Roo.form.Field.superclass.onResize.apply(this, arguments);
28962     },
28963
28964     initEvents : function(){
28965         // Roo.form.Checkbox.superclass.initEvents.call(this);
28966         // has no events...
28967        
28968     },
28969
28970
28971     getResizeEl : function(){
28972         return this.wrap;
28973     },
28974
28975     getPositionEl : function(){
28976         return this.wrap;
28977     },
28978
28979     // private
28980     onRender : function(ct, position){
28981         
28982         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28983         var style = this.style;
28984         delete this.style;
28985         
28986         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28987         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28988         this.viewEl = this.wrap.createChild({ tag: 'div' });
28989         if (style) {
28990             this.viewEl.applyStyles(style);
28991         }
28992         if (this.width) {
28993             this.viewEl.setWidth(this.width);
28994         }
28995         if (this.height) {
28996             this.viewEl.setHeight(this.height);
28997         }
28998         //if(this.inputValue !== undefined){
28999         //this.setValue(this.value);
29000         
29001         
29002         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
29003         
29004         
29005         this.grid.render();
29006         this.grid.getDataSource().on('remove', this.refreshValue, this);
29007         this.grid.getDataSource().on('update', this.refreshValue, this);
29008         this.grid.on('afteredit', this.refreshValue, this);
29009  
29010     },
29011      
29012     
29013     /**
29014      * Sets the value of the item. 
29015      * @param {String} either an object  or a string..
29016      */
29017     setValue : function(v){
29018         //this.value = v;
29019         v = v || []; // empty set..
29020         // this does not seem smart - it really only affects memoryproxy grids..
29021         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
29022             var ds = this.grid.getDataSource();
29023             // assumes a json reader..
29024             var data = {}
29025             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
29026             ds.loadData( data);
29027         }
29028         // clear selection so it does not get stale.
29029         if (this.grid.sm) { 
29030             this.grid.sm.clearSelections();
29031         }
29032         
29033         Roo.form.GridField.superclass.setValue.call(this, v);
29034         this.refreshValue();
29035         // should load data in the grid really....
29036     },
29037     
29038     // private
29039     refreshValue: function() {
29040          var val = [];
29041         this.grid.getDataSource().each(function(r) {
29042             val.push(r.data);
29043         });
29044         this.el.dom.value = Roo.encode(val);
29045     }
29046     
29047      
29048     
29049     
29050 });/*
29051  * Based on:
29052  * Ext JS Library 1.1.1
29053  * Copyright(c) 2006-2007, Ext JS, LLC.
29054  *
29055  * Originally Released Under LGPL - original licence link has changed is not relivant.
29056  *
29057  * Fork - LGPL
29058  * <script type="text/javascript">
29059  */
29060 /**
29061  * @class Roo.form.DisplayField
29062  * @extends Roo.form.Field
29063  * A generic Field to display non-editable data.
29064  * @constructor
29065  * Creates a new Display Field item.
29066  * @param {Object} config Configuration options
29067  */
29068 Roo.form.DisplayField = function(config){
29069     Roo.form.DisplayField.superclass.constructor.call(this, config);
29070     
29071 };
29072
29073 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
29074     inputType:      'hidden',
29075     allowBlank:     true,
29076     readOnly:         true,
29077     
29078  
29079     /**
29080      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29081      */
29082     focusClass : undefined,
29083     /**
29084      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29085      */
29086     fieldClass: 'x-form-field',
29087     
29088      /**
29089      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
29090      */
29091     valueRenderer: undefined,
29092     
29093     width: 100,
29094     /**
29095      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29096      * {tag: "input", type: "checkbox", autocomplete: "off"})
29097      */
29098      
29099  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29100
29101     onResize : function(){
29102         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
29103         
29104     },
29105
29106     initEvents : function(){
29107         // Roo.form.Checkbox.superclass.initEvents.call(this);
29108         // has no events...
29109        
29110     },
29111
29112
29113     getResizeEl : function(){
29114         return this.wrap;
29115     },
29116
29117     getPositionEl : function(){
29118         return this.wrap;
29119     },
29120
29121     // private
29122     onRender : function(ct, position){
29123         
29124         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
29125         //if(this.inputValue !== undefined){
29126         this.wrap = this.el.wrap();
29127         
29128         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
29129         
29130         if (this.bodyStyle) {
29131             this.viewEl.applyStyles(this.bodyStyle);
29132         }
29133         //this.viewEl.setStyle('padding', '2px');
29134         
29135         this.setValue(this.value);
29136         
29137     },
29138 /*
29139     // private
29140     initValue : Roo.emptyFn,
29141
29142   */
29143
29144         // private
29145     onClick : function(){
29146         
29147     },
29148
29149     /**
29150      * Sets the checked state of the checkbox.
29151      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
29152      */
29153     setValue : function(v){
29154         this.value = v;
29155         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
29156         // this might be called before we have a dom element..
29157         if (!this.viewEl) {
29158             return;
29159         }
29160         this.viewEl.dom.innerHTML = html;
29161         Roo.form.DisplayField.superclass.setValue.call(this, v);
29162
29163     }
29164 });/*
29165  * 
29166  * Licence- LGPL
29167  * 
29168  */
29169
29170 /**
29171  * @class Roo.form.DayPicker
29172  * @extends Roo.form.Field
29173  * A Day picker show [M] [T] [W] ....
29174  * @constructor
29175  * Creates a new Day Picker
29176  * @param {Object} config Configuration options
29177  */
29178 Roo.form.DayPicker= function(config){
29179     Roo.form.DayPicker.superclass.constructor.call(this, config);
29180      
29181 };
29182
29183 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
29184     /**
29185      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29186      */
29187     focusClass : undefined,
29188     /**
29189      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29190      */
29191     fieldClass: "x-form-field",
29192    
29193     /**
29194      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29195      * {tag: "input", type: "checkbox", autocomplete: "off"})
29196      */
29197     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
29198     
29199    
29200     actionMode : 'viewEl', 
29201     //
29202     // private
29203  
29204     inputType : 'hidden',
29205     
29206      
29207     inputElement: false, // real input element?
29208     basedOn: false, // ????
29209     
29210     isFormField: true, // not sure where this is needed!!!!
29211
29212     onResize : function(){
29213         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
29214         if(!this.boxLabel){
29215             this.el.alignTo(this.wrap, 'c-c');
29216         }
29217     },
29218
29219     initEvents : function(){
29220         Roo.form.Checkbox.superclass.initEvents.call(this);
29221         this.el.on("click", this.onClick,  this);
29222         this.el.on("change", this.onClick,  this);
29223     },
29224
29225
29226     getResizeEl : function(){
29227         return this.wrap;
29228     },
29229
29230     getPositionEl : function(){
29231         return this.wrap;
29232     },
29233
29234     
29235     // private
29236     onRender : function(ct, position){
29237         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
29238        
29239         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
29240         
29241         var r1 = '<table><tr>';
29242         var r2 = '<tr class="x-form-daypick-icons">';
29243         for (var i=0; i < 7; i++) {
29244             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
29245             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
29246         }
29247         
29248         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
29249         viewEl.select('img').on('click', this.onClick, this);
29250         this.viewEl = viewEl;   
29251         
29252         
29253         // this will not work on Chrome!!!
29254         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
29255         this.el.on('propertychange', this.setFromHidden,  this);  //ie
29256         
29257         
29258           
29259
29260     },
29261
29262     // private
29263     initValue : Roo.emptyFn,
29264
29265     /**
29266      * Returns the checked state of the checkbox.
29267      * @return {Boolean} True if checked, else false
29268      */
29269     getValue : function(){
29270         return this.el.dom.value;
29271         
29272     },
29273
29274         // private
29275     onClick : function(e){ 
29276         //this.setChecked(!this.checked);
29277         Roo.get(e.target).toggleClass('x-menu-item-checked');
29278         this.refreshValue();
29279         //if(this.el.dom.checked != this.checked){
29280         //    this.setValue(this.el.dom.checked);
29281        // }
29282     },
29283     
29284     // private
29285     refreshValue : function()
29286     {
29287         var val = '';
29288         this.viewEl.select('img',true).each(function(e,i,n)  {
29289             val += e.is(".x-menu-item-checked") ? String(n) : '';
29290         });
29291         this.setValue(val, true);
29292     },
29293
29294     /**
29295      * Sets the checked state of the checkbox.
29296      * On is always based on a string comparison between inputValue and the param.
29297      * @param {Boolean/String} value - the value to set 
29298      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
29299      */
29300     setValue : function(v,suppressEvent){
29301         if (!this.el.dom) {
29302             return;
29303         }
29304         var old = this.el.dom.value ;
29305         this.el.dom.value = v;
29306         if (suppressEvent) {
29307             return ;
29308         }
29309          
29310         // update display..
29311         this.viewEl.select('img',true).each(function(e,i,n)  {
29312             
29313             var on = e.is(".x-menu-item-checked");
29314             var newv = v.indexOf(String(n)) > -1;
29315             if (on != newv) {
29316                 e.toggleClass('x-menu-item-checked');
29317             }
29318             
29319         });
29320         
29321         
29322         this.fireEvent('change', this, v, old);
29323         
29324         
29325     },
29326    
29327     // handle setting of hidden value by some other method!!?!?
29328     setFromHidden: function()
29329     {
29330         if(!this.el){
29331             return;
29332         }
29333         //console.log("SET FROM HIDDEN");
29334         //alert('setFrom hidden');
29335         this.setValue(this.el.dom.value);
29336     },
29337     
29338     onDestroy : function()
29339     {
29340         if(this.viewEl){
29341             Roo.get(this.viewEl).remove();
29342         }
29343          
29344         Roo.form.DayPicker.superclass.onDestroy.call(this);
29345     }
29346
29347 });/*
29348  * RooJS Library 1.1.1
29349  * Copyright(c) 2008-2011  Alan Knowles
29350  *
29351  * License - LGPL
29352  */
29353  
29354
29355 /**
29356  * @class Roo.form.ComboCheck
29357  * @extends Roo.form.ComboBox
29358  * A combobox for multiple select items.
29359  *
29360  * FIXME - could do with a reset button..
29361  * 
29362  * @constructor
29363  * Create a new ComboCheck
29364  * @param {Object} config Configuration options
29365  */
29366 Roo.form.ComboCheck = function(config){
29367     Roo.form.ComboCheck.superclass.constructor.call(this, config);
29368     // should verify some data...
29369     // like
29370     // hiddenName = required..
29371     // displayField = required
29372     // valudField == required
29373     var req= [ 'hiddenName', 'displayField', 'valueField' ];
29374     var _t = this;
29375     Roo.each(req, function(e) {
29376         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
29377             throw "Roo.form.ComboCheck : missing value for: " + e;
29378         }
29379     });
29380     
29381     
29382 };
29383
29384 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
29385      
29386      
29387     editable : false,
29388      
29389     selectedClass: 'x-menu-item-checked', 
29390     
29391     // private
29392     onRender : function(ct, position){
29393         var _t = this;
29394         
29395         
29396         
29397         if(!this.tpl){
29398             var cls = 'x-combo-list';
29399
29400             
29401             this.tpl =  new Roo.Template({
29402                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
29403                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
29404                    '<span>{' + this.displayField + '}</span>' +
29405                     '</div>' 
29406                 
29407             });
29408         }
29409  
29410         
29411         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
29412         this.view.singleSelect = false;
29413         this.view.multiSelect = true;
29414         this.view.toggleSelect = true;
29415         this.pageTb.add(new Roo.Toolbar.Fill(), {
29416             
29417             text: 'Done',
29418             handler: function()
29419             {
29420                 _t.collapse();
29421             }
29422         });
29423     },
29424     
29425     onViewOver : function(e, t){
29426         // do nothing...
29427         return;
29428         
29429     },
29430     
29431     onViewClick : function(doFocus,index){
29432         return;
29433         
29434     },
29435     select: function () {
29436         //Roo.log("SELECT CALLED");
29437     },
29438      
29439     selectByValue : function(xv, scrollIntoView){
29440         var ar = this.getValueArray();
29441         var sels = [];
29442         
29443         Roo.each(ar, function(v) {
29444             if(v === undefined || v === null){
29445                 return;
29446             }
29447             var r = this.findRecord(this.valueField, v);
29448             if(r){
29449                 sels.push(this.store.indexOf(r))
29450                 
29451             }
29452         },this);
29453         this.view.select(sels);
29454         return false;
29455     },
29456     
29457     
29458     
29459     onSelect : function(record, index){
29460        // Roo.log("onselect Called");
29461        // this is only called by the clear button now..
29462         this.view.clearSelections();
29463         this.setValue('[]');
29464         if (this.value != this.valueBefore) {
29465             this.fireEvent('change', this, this.value, this.valueBefore);
29466         }
29467     },
29468     getValueArray : function()
29469     {
29470         var ar = [] ;
29471         
29472         try {
29473             //Roo.log(this.value);
29474             if (typeof(this.value) == 'undefined') {
29475                 return [];
29476             }
29477             var ar = Roo.decode(this.value);
29478             return  ar instanceof Array ? ar : []; //?? valid?
29479             
29480         } catch(e) {
29481             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
29482             return [];
29483         }
29484          
29485     },
29486     expand : function ()
29487     {
29488         Roo.form.ComboCheck.superclass.expand.call(this);
29489         this.valueBefore = this.value;
29490         
29491
29492     },
29493     
29494     collapse : function(){
29495         Roo.form.ComboCheck.superclass.collapse.call(this);
29496         var sl = this.view.getSelectedIndexes();
29497         var st = this.store;
29498         var nv = [];
29499         var tv = [];
29500         var r;
29501         Roo.each(sl, function(i) {
29502             r = st.getAt(i);
29503             nv.push(r.get(this.valueField));
29504         },this);
29505         this.setValue(Roo.encode(nv));
29506         if (this.value != this.valueBefore) {
29507
29508             this.fireEvent('change', this, this.value, this.valueBefore);
29509         }
29510         
29511     },
29512     
29513     setValue : function(v){
29514         // Roo.log(v);
29515         this.value = v;
29516         
29517         var vals = this.getValueArray();
29518         var tv = [];
29519         Roo.each(vals, function(k) {
29520             var r = this.findRecord(this.valueField, k);
29521             if(r){
29522                 tv.push(r.data[this.displayField]);
29523             }else if(this.valueNotFoundText !== undefined){
29524                 tv.push( this.valueNotFoundText );
29525             }
29526         },this);
29527        // Roo.log(tv);
29528         
29529         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
29530         this.hiddenField.value = v;
29531         this.value = v;
29532     }
29533     
29534 });//<script type="text/javasscript">
29535  
29536
29537 /**
29538  * @class Roo.DDView
29539  * A DnD enabled version of Roo.View.
29540  * @param {Element/String} container The Element in which to create the View.
29541  * @param {String} tpl The template string used to create the markup for each element of the View
29542  * @param {Object} config The configuration properties. These include all the config options of
29543  * {@link Roo.View} plus some specific to this class.<br>
29544  * <p>
29545  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
29546  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
29547  * <p>
29548  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
29549 .x-view-drag-insert-above {
29550         border-top:1px dotted #3366cc;
29551 }
29552 .x-view-drag-insert-below {
29553         border-bottom:1px dotted #3366cc;
29554 }
29555 </code></pre>
29556  * 
29557  */
29558  
29559 Roo.DDView = function(container, tpl, config) {
29560     Roo.DDView.superclass.constructor.apply(this, arguments);
29561     this.getEl().setStyle("outline", "0px none");
29562     this.getEl().unselectable();
29563     if (this.dragGroup) {
29564                 this.setDraggable(this.dragGroup.split(","));
29565     }
29566     if (this.dropGroup) {
29567                 this.setDroppable(this.dropGroup.split(","));
29568     }
29569     if (this.deletable) {
29570         this.setDeletable();
29571     }
29572     this.isDirtyFlag = false;
29573         this.addEvents({
29574                 "drop" : true
29575         });
29576 };
29577
29578 Roo.extend(Roo.DDView, Roo.View, {
29579 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
29580 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
29581 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
29582 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
29583
29584         isFormField: true,
29585
29586         reset: Roo.emptyFn,
29587         
29588         clearInvalid: Roo.form.Field.prototype.clearInvalid,
29589
29590         validate: function() {
29591                 return true;
29592         },
29593         
29594         destroy: function() {
29595                 this.purgeListeners();
29596                 this.getEl.removeAllListeners();
29597                 this.getEl().remove();
29598                 if (this.dragZone) {
29599                         if (this.dragZone.destroy) {
29600                                 this.dragZone.destroy();
29601                         }
29602                 }
29603                 if (this.dropZone) {
29604                         if (this.dropZone.destroy) {
29605                                 this.dropZone.destroy();
29606                         }
29607                 }
29608         },
29609
29610 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
29611         getName: function() {
29612                 return this.name;
29613         },
29614
29615 /**     Loads the View from a JSON string representing the Records to put into the Store. */
29616         setValue: function(v) {
29617                 if (!this.store) {
29618                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
29619                 }
29620                 var data = {};
29621                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
29622                 this.store.proxy = new Roo.data.MemoryProxy(data);
29623                 this.store.load();
29624         },
29625
29626 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
29627         getValue: function() {
29628                 var result = '(';
29629                 this.store.each(function(rec) {
29630                         result += rec.id + ',';
29631                 });
29632                 return result.substr(0, result.length - 1) + ')';
29633         },
29634         
29635         getIds: function() {
29636                 var i = 0, result = new Array(this.store.getCount());
29637                 this.store.each(function(rec) {
29638                         result[i++] = rec.id;
29639                 });
29640                 return result;
29641         },
29642         
29643         isDirty: function() {
29644                 return this.isDirtyFlag;
29645         },
29646
29647 /**
29648  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
29649  *      whole Element becomes the target, and this causes the drop gesture to append.
29650  */
29651     getTargetFromEvent : function(e) {
29652                 var target = e.getTarget();
29653                 while ((target !== null) && (target.parentNode != this.el.dom)) {
29654                 target = target.parentNode;
29655                 }
29656                 if (!target) {
29657                         target = this.el.dom.lastChild || this.el.dom;
29658                 }
29659                 return target;
29660     },
29661
29662 /**
29663  *      Create the drag data which consists of an object which has the property "ddel" as
29664  *      the drag proxy element. 
29665  */
29666     getDragData : function(e) {
29667         var target = this.findItemFromChild(e.getTarget());
29668                 if(target) {
29669                         this.handleSelection(e);
29670                         var selNodes = this.getSelectedNodes();
29671             var dragData = {
29672                 source: this,
29673                 copy: this.copy || (this.allowCopy && e.ctrlKey),
29674                 nodes: selNodes,
29675                 records: []
29676                         };
29677                         var selectedIndices = this.getSelectedIndexes();
29678                         for (var i = 0; i < selectedIndices.length; i++) {
29679                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
29680                         }
29681                         if (selNodes.length == 1) {
29682                                 dragData.ddel = target.cloneNode(true); // the div element
29683                         } else {
29684                                 var div = document.createElement('div'); // create the multi element drag "ghost"
29685                                 div.className = 'multi-proxy';
29686                                 for (var i = 0, len = selNodes.length; i < len; i++) {
29687                                         div.appendChild(selNodes[i].cloneNode(true));
29688                                 }
29689                                 dragData.ddel = div;
29690                         }
29691             //console.log(dragData)
29692             //console.log(dragData.ddel.innerHTML)
29693                         return dragData;
29694                 }
29695         //console.log('nodragData')
29696                 return false;
29697     },
29698     
29699 /**     Specify to which ddGroup items in this DDView may be dragged. */
29700     setDraggable: function(ddGroup) {
29701         if (ddGroup instanceof Array) {
29702                 Roo.each(ddGroup, this.setDraggable, this);
29703                 return;
29704         }
29705         if (this.dragZone) {
29706                 this.dragZone.addToGroup(ddGroup);
29707         } else {
29708                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
29709                                 containerScroll: true,
29710                                 ddGroup: ddGroup 
29711
29712                         });
29713 //                      Draggability implies selection. DragZone's mousedown selects the element.
29714                         if (!this.multiSelect) { this.singleSelect = true; }
29715
29716 //                      Wire the DragZone's handlers up to methods in *this*
29717                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
29718                 }
29719     },
29720
29721 /**     Specify from which ddGroup this DDView accepts drops. */
29722     setDroppable: function(ddGroup) {
29723         if (ddGroup instanceof Array) {
29724                 Roo.each(ddGroup, this.setDroppable, this);
29725                 return;
29726         }
29727         if (this.dropZone) {
29728                 this.dropZone.addToGroup(ddGroup);
29729         } else {
29730                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
29731                                 containerScroll: true,
29732                                 ddGroup: ddGroup
29733                         });
29734
29735 //                      Wire the DropZone's handlers up to methods in *this*
29736                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
29737                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
29738                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
29739                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
29740                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
29741                 }
29742     },
29743
29744 /**     Decide whether to drop above or below a View node. */
29745     getDropPoint : function(e, n, dd){
29746         if (n == this.el.dom) { return "above"; }
29747                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
29748                 var c = t + (b - t) / 2;
29749                 var y = Roo.lib.Event.getPageY(e);
29750                 if(y <= c) {
29751                         return "above";
29752                 }else{
29753                         return "below";
29754                 }
29755     },
29756
29757     onNodeEnter : function(n, dd, e, data){
29758                 return false;
29759     },
29760     
29761     onNodeOver : function(n, dd, e, data){
29762                 var pt = this.getDropPoint(e, n, dd);
29763                 // set the insert point style on the target node
29764                 var dragElClass = this.dropNotAllowed;
29765                 if (pt) {
29766                         var targetElClass;
29767                         if (pt == "above"){
29768                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
29769                                 targetElClass = "x-view-drag-insert-above";
29770                         } else {
29771                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
29772                                 targetElClass = "x-view-drag-insert-below";
29773                         }
29774                         if (this.lastInsertClass != targetElClass){
29775                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
29776                                 this.lastInsertClass = targetElClass;
29777                         }
29778                 }
29779                 return dragElClass;
29780         },
29781
29782     onNodeOut : function(n, dd, e, data){
29783                 this.removeDropIndicators(n);
29784     },
29785
29786     onNodeDrop : function(n, dd, e, data){
29787         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
29788                 return false;
29789         }
29790         var pt = this.getDropPoint(e, n, dd);
29791                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
29792                 if (pt == "below") { insertAt++; }
29793                 for (var i = 0; i < data.records.length; i++) {
29794                         var r = data.records[i];
29795                         var dup = this.store.getById(r.id);
29796                         if (dup && (dd != this.dragZone)) {
29797                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
29798                         } else {
29799                                 if (data.copy) {
29800                                         this.store.insert(insertAt++, r.copy());
29801                                 } else {
29802                                         data.source.isDirtyFlag = true;
29803                                         r.store.remove(r);
29804                                         this.store.insert(insertAt++, r);
29805                                 }
29806                                 this.isDirtyFlag = true;
29807                         }
29808                 }
29809                 this.dragZone.cachedTarget = null;
29810                 return true;
29811     },
29812
29813     removeDropIndicators : function(n){
29814                 if(n){
29815                         Roo.fly(n).removeClass([
29816                                 "x-view-drag-insert-above",
29817                                 "x-view-drag-insert-below"]);
29818                         this.lastInsertClass = "_noclass";
29819                 }
29820     },
29821
29822 /**
29823  *      Utility method. Add a delete option to the DDView's context menu.
29824  *      @param {String} imageUrl The URL of the "delete" icon image.
29825  */
29826         setDeletable: function(imageUrl) {
29827                 if (!this.singleSelect && !this.multiSelect) {
29828                         this.singleSelect = true;
29829                 }
29830                 var c = this.getContextMenu();
29831                 this.contextMenu.on("itemclick", function(item) {
29832                         switch (item.id) {
29833                                 case "delete":
29834                                         this.remove(this.getSelectedIndexes());
29835                                         break;
29836                         }
29837                 }, this);
29838                 this.contextMenu.add({
29839                         icon: imageUrl,
29840                         id: "delete",
29841                         text: 'Delete'
29842                 });
29843         },
29844         
29845 /**     Return the context menu for this DDView. */
29846         getContextMenu: function() {
29847                 if (!this.contextMenu) {
29848 //                      Create the View's context menu
29849                         this.contextMenu = new Roo.menu.Menu({
29850                                 id: this.id + "-contextmenu"
29851                         });
29852                         this.el.on("contextmenu", this.showContextMenu, this);
29853                 }
29854                 return this.contextMenu;
29855         },
29856         
29857         disableContextMenu: function() {
29858                 if (this.contextMenu) {
29859                         this.el.un("contextmenu", this.showContextMenu, this);
29860                 }
29861         },
29862
29863         showContextMenu: function(e, item) {
29864         item = this.findItemFromChild(e.getTarget());
29865                 if (item) {
29866                         e.stopEvent();
29867                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
29868                         this.contextMenu.showAt(e.getXY());
29869             }
29870     },
29871
29872 /**
29873  *      Remove {@link Roo.data.Record}s at the specified indices.
29874  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
29875  */
29876     remove: function(selectedIndices) {
29877                 selectedIndices = [].concat(selectedIndices);
29878                 for (var i = 0; i < selectedIndices.length; i++) {
29879                         var rec = this.store.getAt(selectedIndices[i]);
29880                         this.store.remove(rec);
29881                 }
29882     },
29883
29884 /**
29885  *      Double click fires the event, but also, if this is draggable, and there is only one other
29886  *      related DropZone, it transfers the selected node.
29887  */
29888     onDblClick : function(e){
29889         var item = this.findItemFromChild(e.getTarget());
29890         if(item){
29891             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
29892                 return false;
29893             }
29894             if (this.dragGroup) {
29895                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
29896                     while (targets.indexOf(this.dropZone) > -1) {
29897                             targets.remove(this.dropZone);
29898                                 }
29899                     if (targets.length == 1) {
29900                                         this.dragZone.cachedTarget = null;
29901                         var el = Roo.get(targets[0].getEl());
29902                         var box = el.getBox(true);
29903                         targets[0].onNodeDrop(el.dom, {
29904                                 target: el.dom,
29905                                 xy: [box.x, box.y + box.height - 1]
29906                         }, null, this.getDragData(e));
29907                     }
29908                 }
29909         }
29910     },
29911     
29912     handleSelection: function(e) {
29913                 this.dragZone.cachedTarget = null;
29914         var item = this.findItemFromChild(e.getTarget());
29915         if (!item) {
29916                 this.clearSelections(true);
29917                 return;
29918         }
29919                 if (item && (this.multiSelect || this.singleSelect)){
29920                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
29921                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
29922                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
29923                                 this.unselect(item);
29924                         } else {
29925                                 this.select(item, this.multiSelect && e.ctrlKey);
29926                                 this.lastSelection = item;
29927                         }
29928                 }
29929     },
29930
29931     onItemClick : function(item, index, e){
29932                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
29933                         return false;
29934                 }
29935                 return true;
29936     },
29937
29938     unselect : function(nodeInfo, suppressEvent){
29939                 var node = this.getNode(nodeInfo);
29940                 if(node && this.isSelected(node)){
29941                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
29942                                 Roo.fly(node).removeClass(this.selectedClass);
29943                                 this.selections.remove(node);
29944                                 if(!suppressEvent){
29945                                         this.fireEvent("selectionchange", this, this.selections);
29946                                 }
29947                         }
29948                 }
29949     }
29950 });
29951 /*
29952  * Based on:
29953  * Ext JS Library 1.1.1
29954  * Copyright(c) 2006-2007, Ext JS, LLC.
29955  *
29956  * Originally Released Under LGPL - original licence link has changed is not relivant.
29957  *
29958  * Fork - LGPL
29959  * <script type="text/javascript">
29960  */
29961  
29962 /**
29963  * @class Roo.LayoutManager
29964  * @extends Roo.util.Observable
29965  * Base class for layout managers.
29966  */
29967 Roo.LayoutManager = function(container, config){
29968     Roo.LayoutManager.superclass.constructor.call(this);
29969     this.el = Roo.get(container);
29970     // ie scrollbar fix
29971     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
29972         document.body.scroll = "no";
29973     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
29974         this.el.position('relative');
29975     }
29976     this.id = this.el.id;
29977     this.el.addClass("x-layout-container");
29978     /** false to disable window resize monitoring @type Boolean */
29979     this.monitorWindowResize = true;
29980     this.regions = {};
29981     this.addEvents({
29982         /**
29983          * @event layout
29984          * Fires when a layout is performed. 
29985          * @param {Roo.LayoutManager} this
29986          */
29987         "layout" : true,
29988         /**
29989          * @event regionresized
29990          * Fires when the user resizes a region. 
29991          * @param {Roo.LayoutRegion} region The resized region
29992          * @param {Number} newSize The new size (width for east/west, height for north/south)
29993          */
29994         "regionresized" : true,
29995         /**
29996          * @event regioncollapsed
29997          * Fires when a region is collapsed. 
29998          * @param {Roo.LayoutRegion} region The collapsed region
29999          */
30000         "regioncollapsed" : true,
30001         /**
30002          * @event regionexpanded
30003          * Fires when a region is expanded.  
30004          * @param {Roo.LayoutRegion} region The expanded region
30005          */
30006         "regionexpanded" : true
30007     });
30008     this.updating = false;
30009     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
30010 };
30011
30012 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
30013     /**
30014      * Returns true if this layout is currently being updated
30015      * @return {Boolean}
30016      */
30017     isUpdating : function(){
30018         return this.updating; 
30019     },
30020     
30021     /**
30022      * Suspend the LayoutManager from doing auto-layouts while
30023      * making multiple add or remove calls
30024      */
30025     beginUpdate : function(){
30026         this.updating = true;    
30027     },
30028     
30029     /**
30030      * Restore auto-layouts and optionally disable the manager from performing a layout
30031      * @param {Boolean} noLayout true to disable a layout update 
30032      */
30033     endUpdate : function(noLayout){
30034         this.updating = false;
30035         if(!noLayout){
30036             this.layout();
30037         }    
30038     },
30039     
30040     layout: function(){
30041         
30042     },
30043     
30044     onRegionResized : function(region, newSize){
30045         this.fireEvent("regionresized", region, newSize);
30046         this.layout();
30047     },
30048     
30049     onRegionCollapsed : function(region){
30050         this.fireEvent("regioncollapsed", region);
30051     },
30052     
30053     onRegionExpanded : function(region){
30054         this.fireEvent("regionexpanded", region);
30055     },
30056         
30057     /**
30058      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
30059      * performs box-model adjustments.
30060      * @return {Object} The size as an object {width: (the width), height: (the height)}
30061      */
30062     getViewSize : function(){
30063         var size;
30064         if(this.el.dom != document.body){
30065             size = this.el.getSize();
30066         }else{
30067             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
30068         }
30069         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
30070         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30071         return size;
30072     },
30073     
30074     /**
30075      * Returns the Element this layout is bound to.
30076      * @return {Roo.Element}
30077      */
30078     getEl : function(){
30079         return this.el;
30080     },
30081     
30082     /**
30083      * Returns the specified region.
30084      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
30085      * @return {Roo.LayoutRegion}
30086      */
30087     getRegion : function(target){
30088         return this.regions[target.toLowerCase()];
30089     },
30090     
30091     onWindowResize : function(){
30092         if(this.monitorWindowResize){
30093             this.layout();
30094         }
30095     }
30096 });/*
30097  * Based on:
30098  * Ext JS Library 1.1.1
30099  * Copyright(c) 2006-2007, Ext JS, LLC.
30100  *
30101  * Originally Released Under LGPL - original licence link has changed is not relivant.
30102  *
30103  * Fork - LGPL
30104  * <script type="text/javascript">
30105  */
30106 /**
30107  * @class Roo.BorderLayout
30108  * @extends Roo.LayoutManager
30109  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
30110  * please see: <br><br>
30111  * <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>
30112  * <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>
30113  * Example:
30114  <pre><code>
30115  var layout = new Roo.BorderLayout(document.body, {
30116     north: {
30117         initialSize: 25,
30118         titlebar: false
30119     },
30120     west: {
30121         split:true,
30122         initialSize: 200,
30123         minSize: 175,
30124         maxSize: 400,
30125         titlebar: true,
30126         collapsible: true
30127     },
30128     east: {
30129         split:true,
30130         initialSize: 202,
30131         minSize: 175,
30132         maxSize: 400,
30133         titlebar: true,
30134         collapsible: true
30135     },
30136     south: {
30137         split:true,
30138         initialSize: 100,
30139         minSize: 100,
30140         maxSize: 200,
30141         titlebar: true,
30142         collapsible: true
30143     },
30144     center: {
30145         titlebar: true,
30146         autoScroll:true,
30147         resizeTabs: true,
30148         minTabWidth: 50,
30149         preferredTabWidth: 150
30150     }
30151 });
30152
30153 // shorthand
30154 var CP = Roo.ContentPanel;
30155
30156 layout.beginUpdate();
30157 layout.add("north", new CP("north", "North"));
30158 layout.add("south", new CP("south", {title: "South", closable: true}));
30159 layout.add("west", new CP("west", {title: "West"}));
30160 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
30161 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
30162 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
30163 layout.getRegion("center").showPanel("center1");
30164 layout.endUpdate();
30165 </code></pre>
30166
30167 <b>The container the layout is rendered into can be either the body element or any other element.
30168 If it is not the body element, the container needs to either be an absolute positioned element,
30169 or you will need to add "position:relative" to the css of the container.  You will also need to specify
30170 the container size if it is not the body element.</b>
30171
30172 * @constructor
30173 * Create a new BorderLayout
30174 * @param {String/HTMLElement/Element} container The container this layout is bound to
30175 * @param {Object} config Configuration options
30176  */
30177 Roo.BorderLayout = function(container, config){
30178     config = config || {};
30179     Roo.BorderLayout.superclass.constructor.call(this, container, config);
30180     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
30181     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
30182         var target = this.factory.validRegions[i];
30183         if(config[target]){
30184             this.addRegion(target, config[target]);
30185         }
30186     }
30187 };
30188
30189 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
30190     /**
30191      * Creates and adds a new region if it doesn't already exist.
30192      * @param {String} target The target region key (north, south, east, west or center).
30193      * @param {Object} config The regions config object
30194      * @return {BorderLayoutRegion} The new region
30195      */
30196     addRegion : function(target, config){
30197         if(!this.regions[target]){
30198             var r = this.factory.create(target, this, config);
30199             this.bindRegion(target, r);
30200         }
30201         return this.regions[target];
30202     },
30203
30204     // private (kinda)
30205     bindRegion : function(name, r){
30206         this.regions[name] = r;
30207         r.on("visibilitychange", this.layout, this);
30208         r.on("paneladded", this.layout, this);
30209         r.on("panelremoved", this.layout, this);
30210         r.on("invalidated", this.layout, this);
30211         r.on("resized", this.onRegionResized, this);
30212         r.on("collapsed", this.onRegionCollapsed, this);
30213         r.on("expanded", this.onRegionExpanded, this);
30214     },
30215
30216     /**
30217      * Performs a layout update.
30218      */
30219     layout : function(){
30220         if(this.updating) return;
30221         var size = this.getViewSize();
30222         var w = size.width;
30223         var h = size.height;
30224         var centerW = w;
30225         var centerH = h;
30226         var centerY = 0;
30227         var centerX = 0;
30228         //var x = 0, y = 0;
30229
30230         var rs = this.regions;
30231         var north = rs["north"];
30232         var south = rs["south"]; 
30233         var west = rs["west"];
30234         var east = rs["east"];
30235         var center = rs["center"];
30236         //if(this.hideOnLayout){ // not supported anymore
30237             //c.el.setStyle("display", "none");
30238         //}
30239         if(north && north.isVisible()){
30240             var b = north.getBox();
30241             var m = north.getMargins();
30242             b.width = w - (m.left+m.right);
30243             b.x = m.left;
30244             b.y = m.top;
30245             centerY = b.height + b.y + m.bottom;
30246             centerH -= centerY;
30247             north.updateBox(this.safeBox(b));
30248         }
30249         if(south && south.isVisible()){
30250             var b = south.getBox();
30251             var m = south.getMargins();
30252             b.width = w - (m.left+m.right);
30253             b.x = m.left;
30254             var totalHeight = (b.height + m.top + m.bottom);
30255             b.y = h - totalHeight + m.top;
30256             centerH -= totalHeight;
30257             south.updateBox(this.safeBox(b));
30258         }
30259         if(west && west.isVisible()){
30260             var b = west.getBox();
30261             var m = west.getMargins();
30262             b.height = centerH - (m.top+m.bottom);
30263             b.x = m.left;
30264             b.y = centerY + m.top;
30265             var totalWidth = (b.width + m.left + m.right);
30266             centerX += totalWidth;
30267             centerW -= totalWidth;
30268             west.updateBox(this.safeBox(b));
30269         }
30270         if(east && east.isVisible()){
30271             var b = east.getBox();
30272             var m = east.getMargins();
30273             b.height = centerH - (m.top+m.bottom);
30274             var totalWidth = (b.width + m.left + m.right);
30275             b.x = w - totalWidth + m.left;
30276             b.y = centerY + m.top;
30277             centerW -= totalWidth;
30278             east.updateBox(this.safeBox(b));
30279         }
30280         if(center){
30281             var m = center.getMargins();
30282             var centerBox = {
30283                 x: centerX + m.left,
30284                 y: centerY + m.top,
30285                 width: centerW - (m.left+m.right),
30286                 height: centerH - (m.top+m.bottom)
30287             };
30288             //if(this.hideOnLayout){
30289                 //center.el.setStyle("display", "block");
30290             //}
30291             center.updateBox(this.safeBox(centerBox));
30292         }
30293         this.el.repaint();
30294         this.fireEvent("layout", this);
30295     },
30296
30297     // private
30298     safeBox : function(box){
30299         box.width = Math.max(0, box.width);
30300         box.height = Math.max(0, box.height);
30301         return box;
30302     },
30303
30304     /**
30305      * Adds a ContentPanel (or subclass) to this layout.
30306      * @param {String} target The target region key (north, south, east, west or center).
30307      * @param {Roo.ContentPanel} panel The panel to add
30308      * @return {Roo.ContentPanel} The added panel
30309      */
30310     add : function(target, panel){
30311          
30312         target = target.toLowerCase();
30313         return this.regions[target].add(panel);
30314     },
30315
30316     /**
30317      * Remove a ContentPanel (or subclass) to this layout.
30318      * @param {String} target The target region key (north, south, east, west or center).
30319      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
30320      * @return {Roo.ContentPanel} The removed panel
30321      */
30322     remove : function(target, panel){
30323         target = target.toLowerCase();
30324         return this.regions[target].remove(panel);
30325     },
30326
30327     /**
30328      * Searches all regions for a panel with the specified id
30329      * @param {String} panelId
30330      * @return {Roo.ContentPanel} The panel or null if it wasn't found
30331      */
30332     findPanel : function(panelId){
30333         var rs = this.regions;
30334         for(var target in rs){
30335             if(typeof rs[target] != "function"){
30336                 var p = rs[target].getPanel(panelId);
30337                 if(p){
30338                     return p;
30339                 }
30340             }
30341         }
30342         return null;
30343     },
30344
30345     /**
30346      * Searches all regions for a panel with the specified id and activates (shows) it.
30347      * @param {String/ContentPanel} panelId The panels id or the panel itself
30348      * @return {Roo.ContentPanel} The shown panel or null
30349      */
30350     showPanel : function(panelId) {
30351       var rs = this.regions;
30352       for(var target in rs){
30353          var r = rs[target];
30354          if(typeof r != "function"){
30355             if(r.hasPanel(panelId)){
30356                return r.showPanel(panelId);
30357             }
30358          }
30359       }
30360       return null;
30361    },
30362
30363    /**
30364      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
30365      * @param {Roo.state.Provider} provider (optional) An alternate state provider
30366      */
30367     restoreState : function(provider){
30368         if(!provider){
30369             provider = Roo.state.Manager;
30370         }
30371         var sm = new Roo.LayoutStateManager();
30372         sm.init(this, provider);
30373     },
30374
30375     /**
30376      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
30377      * object should contain properties for each region to add ContentPanels to, and each property's value should be
30378      * a valid ContentPanel config object.  Example:
30379      * <pre><code>
30380 // Create the main layout
30381 var layout = new Roo.BorderLayout('main-ct', {
30382     west: {
30383         split:true,
30384         minSize: 175,
30385         titlebar: true
30386     },
30387     center: {
30388         title:'Components'
30389     }
30390 }, 'main-ct');
30391
30392 // Create and add multiple ContentPanels at once via configs
30393 layout.batchAdd({
30394    west: {
30395        id: 'source-files',
30396        autoCreate:true,
30397        title:'Ext Source Files',
30398        autoScroll:true,
30399        fitToFrame:true
30400    },
30401    center : {
30402        el: cview,
30403        autoScroll:true,
30404        fitToFrame:true,
30405        toolbar: tb,
30406        resizeEl:'cbody'
30407    }
30408 });
30409 </code></pre>
30410      * @param {Object} regions An object containing ContentPanel configs by region name
30411      */
30412     batchAdd : function(regions){
30413         this.beginUpdate();
30414         for(var rname in regions){
30415             var lr = this.regions[rname];
30416             if(lr){
30417                 this.addTypedPanels(lr, regions[rname]);
30418             }
30419         }
30420         this.endUpdate();
30421     },
30422
30423     // private
30424     addTypedPanels : function(lr, ps){
30425         if(typeof ps == 'string'){
30426             lr.add(new Roo.ContentPanel(ps));
30427         }
30428         else if(ps instanceof Array){
30429             for(var i =0, len = ps.length; i < len; i++){
30430                 this.addTypedPanels(lr, ps[i]);
30431             }
30432         }
30433         else if(!ps.events){ // raw config?
30434             var el = ps.el;
30435             delete ps.el; // prevent conflict
30436             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
30437         }
30438         else {  // panel object assumed!
30439             lr.add(ps);
30440         }
30441     },
30442     /**
30443      * Adds a xtype elements to the layout.
30444      * <pre><code>
30445
30446 layout.addxtype({
30447        xtype : 'ContentPanel',
30448        region: 'west',
30449        items: [ .... ]
30450    }
30451 );
30452
30453 layout.addxtype({
30454         xtype : 'NestedLayoutPanel',
30455         region: 'west',
30456         layout: {
30457            center: { },
30458            west: { }   
30459         },
30460         items : [ ... list of content panels or nested layout panels.. ]
30461    }
30462 );
30463 </code></pre>
30464      * @param {Object} cfg Xtype definition of item to add.
30465      */
30466     addxtype : function(cfg)
30467     {
30468         // basically accepts a pannel...
30469         // can accept a layout region..!?!?
30470         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
30471         
30472         if (!cfg.xtype.match(/Panel$/)) {
30473             return false;
30474         }
30475         var ret = false;
30476         
30477         if (typeof(cfg.region) == 'undefined') {
30478             Roo.log("Failed to add Panel, region was not set");
30479             Roo.log(cfg);
30480             return false;
30481         }
30482         var region = cfg.region;
30483         delete cfg.region;
30484         
30485           
30486         var xitems = [];
30487         if (cfg.items) {
30488             xitems = cfg.items;
30489             delete cfg.items;
30490         }
30491         var nb = false;
30492         
30493         switch(cfg.xtype) 
30494         {
30495             case 'ContentPanel':  // ContentPanel (el, cfg)
30496             case 'ScrollPanel':  // ContentPanel (el, cfg)
30497                 if(cfg.autoCreate) {
30498                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30499                 } else {
30500                     var el = this.el.createChild();
30501                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
30502                 }
30503                 
30504                 this.add(region, ret);
30505                 break;
30506             
30507             
30508             case 'TreePanel': // our new panel!
30509                 cfg.el = this.el.createChild();
30510                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30511                 this.add(region, ret);
30512                 break;
30513             
30514             case 'NestedLayoutPanel': 
30515                 // create a new Layout (which is  a Border Layout...
30516                 var el = this.el.createChild();
30517                 var clayout = cfg.layout;
30518                 delete cfg.layout;
30519                 clayout.items   = clayout.items  || [];
30520                 // replace this exitems with the clayout ones..
30521                 xitems = clayout.items;
30522                  
30523                 
30524                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
30525                     cfg.background = false;
30526                 }
30527                 var layout = new Roo.BorderLayout(el, clayout);
30528                 
30529                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
30530                 //console.log('adding nested layout panel '  + cfg.toSource());
30531                 this.add(region, ret);
30532                 nb = {}; /// find first...
30533                 break;
30534                 
30535             case 'GridPanel': 
30536             
30537                 // needs grid and region
30538                 
30539                 //var el = this.getRegion(region).el.createChild();
30540                 var el = this.el.createChild();
30541                 // create the grid first...
30542                 
30543                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
30544                 delete cfg.grid;
30545                 if (region == 'center' && this.active ) {
30546                     cfg.background = false;
30547                 }
30548                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
30549                 
30550                 this.add(region, ret);
30551                 if (cfg.background) {
30552                     ret.on('activate', function(gp) {
30553                         if (!gp.grid.rendered) {
30554                             gp.grid.render();
30555                         }
30556                     });
30557                 } else {
30558                     grid.render();
30559                 }
30560                 break;
30561            
30562                
30563                 
30564                 
30565             default: 
30566                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
30567                 return null;
30568              // GridPanel (grid, cfg)
30569             
30570         }
30571         this.beginUpdate();
30572         // add children..
30573         var region = '';
30574         var abn = {};
30575         Roo.each(xitems, function(i)  {
30576             region = nb && i.region ? i.region : false;
30577             
30578             var add = ret.addxtype(i);
30579            
30580             if (region) {
30581                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
30582                 if (!i.background) {
30583                     abn[region] = nb[region] ;
30584                 }
30585             }
30586             
30587         });
30588         this.endUpdate();
30589
30590         // make the last non-background panel active..
30591         //if (nb) { Roo.log(abn); }
30592         if (nb) {
30593             
30594             for(var r in abn) {
30595                 region = this.getRegion(r);
30596                 if (region) {
30597                     // tried using nb[r], but it does not work..
30598                      
30599                     region.showPanel(abn[r]);
30600                    
30601                 }
30602             }
30603         }
30604         return ret;
30605         
30606     }
30607 });
30608
30609 /**
30610  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
30611  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
30612  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
30613  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
30614  * <pre><code>
30615 // shorthand
30616 var CP = Roo.ContentPanel;
30617
30618 var layout = Roo.BorderLayout.create({
30619     north: {
30620         initialSize: 25,
30621         titlebar: false,
30622         panels: [new CP("north", "North")]
30623     },
30624     west: {
30625         split:true,
30626         initialSize: 200,
30627         minSize: 175,
30628         maxSize: 400,
30629         titlebar: true,
30630         collapsible: true,
30631         panels: [new CP("west", {title: "West"})]
30632     },
30633     east: {
30634         split:true,
30635         initialSize: 202,
30636         minSize: 175,
30637         maxSize: 400,
30638         titlebar: true,
30639         collapsible: true,
30640         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
30641     },
30642     south: {
30643         split:true,
30644         initialSize: 100,
30645         minSize: 100,
30646         maxSize: 200,
30647         titlebar: true,
30648         collapsible: true,
30649         panels: [new CP("south", {title: "South", closable: true})]
30650     },
30651     center: {
30652         titlebar: true,
30653         autoScroll:true,
30654         resizeTabs: true,
30655         minTabWidth: 50,
30656         preferredTabWidth: 150,
30657         panels: [
30658             new CP("center1", {title: "Close Me", closable: true}),
30659             new CP("center2", {title: "Center Panel", closable: false})
30660         ]
30661     }
30662 }, document.body);
30663
30664 layout.getRegion("center").showPanel("center1");
30665 </code></pre>
30666  * @param config
30667  * @param targetEl
30668  */
30669 Roo.BorderLayout.create = function(config, targetEl){
30670     var layout = new Roo.BorderLayout(targetEl || document.body, config);
30671     layout.beginUpdate();
30672     var regions = Roo.BorderLayout.RegionFactory.validRegions;
30673     for(var j = 0, jlen = regions.length; j < jlen; j++){
30674         var lr = regions[j];
30675         if(layout.regions[lr] && config[lr].panels){
30676             var r = layout.regions[lr];
30677             var ps = config[lr].panels;
30678             layout.addTypedPanels(r, ps);
30679         }
30680     }
30681     layout.endUpdate();
30682     return layout;
30683 };
30684
30685 // private
30686 Roo.BorderLayout.RegionFactory = {
30687     // private
30688     validRegions : ["north","south","east","west","center"],
30689
30690     // private
30691     create : function(target, mgr, config){
30692         target = target.toLowerCase();
30693         if(config.lightweight || config.basic){
30694             return new Roo.BasicLayoutRegion(mgr, config, target);
30695         }
30696         switch(target){
30697             case "north":
30698                 return new Roo.NorthLayoutRegion(mgr, config);
30699             case "south":
30700                 return new Roo.SouthLayoutRegion(mgr, config);
30701             case "east":
30702                 return new Roo.EastLayoutRegion(mgr, config);
30703             case "west":
30704                 return new Roo.WestLayoutRegion(mgr, config);
30705             case "center":
30706                 return new Roo.CenterLayoutRegion(mgr, config);
30707         }
30708         throw 'Layout region "'+target+'" not supported.';
30709     }
30710 };/*
30711  * Based on:
30712  * Ext JS Library 1.1.1
30713  * Copyright(c) 2006-2007, Ext JS, LLC.
30714  *
30715  * Originally Released Under LGPL - original licence link has changed is not relivant.
30716  *
30717  * Fork - LGPL
30718  * <script type="text/javascript">
30719  */
30720  
30721 /**
30722  * @class Roo.BasicLayoutRegion
30723  * @extends Roo.util.Observable
30724  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
30725  * and does not have a titlebar, tabs or any other features. All it does is size and position 
30726  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
30727  */
30728 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
30729     this.mgr = mgr;
30730     this.position  = pos;
30731     this.events = {
30732         /**
30733          * @scope Roo.BasicLayoutRegion
30734          */
30735         
30736         /**
30737          * @event beforeremove
30738          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
30739          * @param {Roo.LayoutRegion} this
30740          * @param {Roo.ContentPanel} panel The panel
30741          * @param {Object} e The cancel event object
30742          */
30743         "beforeremove" : true,
30744         /**
30745          * @event invalidated
30746          * Fires when the layout for this region is changed.
30747          * @param {Roo.LayoutRegion} this
30748          */
30749         "invalidated" : true,
30750         /**
30751          * @event visibilitychange
30752          * Fires when this region is shown or hidden 
30753          * @param {Roo.LayoutRegion} this
30754          * @param {Boolean} visibility true or false
30755          */
30756         "visibilitychange" : true,
30757         /**
30758          * @event paneladded
30759          * Fires when a panel is added. 
30760          * @param {Roo.LayoutRegion} this
30761          * @param {Roo.ContentPanel} panel The panel
30762          */
30763         "paneladded" : true,
30764         /**
30765          * @event panelremoved
30766          * Fires when a panel is removed. 
30767          * @param {Roo.LayoutRegion} this
30768          * @param {Roo.ContentPanel} panel The panel
30769          */
30770         "panelremoved" : true,
30771         /**
30772          * @event collapsed
30773          * Fires when this region is collapsed.
30774          * @param {Roo.LayoutRegion} this
30775          */
30776         "collapsed" : true,
30777         /**
30778          * @event expanded
30779          * Fires when this region is expanded.
30780          * @param {Roo.LayoutRegion} this
30781          */
30782         "expanded" : true,
30783         /**
30784          * @event slideshow
30785          * Fires when this region is slid into view.
30786          * @param {Roo.LayoutRegion} this
30787          */
30788         "slideshow" : true,
30789         /**
30790          * @event slidehide
30791          * Fires when this region slides out of view. 
30792          * @param {Roo.LayoutRegion} this
30793          */
30794         "slidehide" : true,
30795         /**
30796          * @event panelactivated
30797          * Fires when a panel is activated. 
30798          * @param {Roo.LayoutRegion} this
30799          * @param {Roo.ContentPanel} panel The activated panel
30800          */
30801         "panelactivated" : true,
30802         /**
30803          * @event resized
30804          * Fires when the user resizes this region. 
30805          * @param {Roo.LayoutRegion} this
30806          * @param {Number} newSize The new size (width for east/west, height for north/south)
30807          */
30808         "resized" : true
30809     };
30810     /** A collection of panels in this region. @type Roo.util.MixedCollection */
30811     this.panels = new Roo.util.MixedCollection();
30812     this.panels.getKey = this.getPanelId.createDelegate(this);
30813     this.box = null;
30814     this.activePanel = null;
30815     // ensure listeners are added...
30816     
30817     if (config.listeners || config.events) {
30818         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
30819             listeners : config.listeners || {},
30820             events : config.events || {}
30821         });
30822     }
30823     
30824     if(skipConfig !== true){
30825         this.applyConfig(config);
30826     }
30827 };
30828
30829 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
30830     getPanelId : function(p){
30831         return p.getId();
30832     },
30833     
30834     applyConfig : function(config){
30835         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30836         this.config = config;
30837         
30838     },
30839     
30840     /**
30841      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
30842      * the width, for horizontal (north, south) the height.
30843      * @param {Number} newSize The new width or height
30844      */
30845     resizeTo : function(newSize){
30846         var el = this.el ? this.el :
30847                  (this.activePanel ? this.activePanel.getEl() : null);
30848         if(el){
30849             switch(this.position){
30850                 case "east":
30851                 case "west":
30852                     el.setWidth(newSize);
30853                     this.fireEvent("resized", this, newSize);
30854                 break;
30855                 case "north":
30856                 case "south":
30857                     el.setHeight(newSize);
30858                     this.fireEvent("resized", this, newSize);
30859                 break;                
30860             }
30861         }
30862     },
30863     
30864     getBox : function(){
30865         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
30866     },
30867     
30868     getMargins : function(){
30869         return this.margins;
30870     },
30871     
30872     updateBox : function(box){
30873         this.box = box;
30874         var el = this.activePanel.getEl();
30875         el.dom.style.left = box.x + "px";
30876         el.dom.style.top = box.y + "px";
30877         this.activePanel.setSize(box.width, box.height);
30878     },
30879     
30880     /**
30881      * Returns the container element for this region.
30882      * @return {Roo.Element}
30883      */
30884     getEl : function(){
30885         return this.activePanel;
30886     },
30887     
30888     /**
30889      * Returns true if this region is currently visible.
30890      * @return {Boolean}
30891      */
30892     isVisible : function(){
30893         return this.activePanel ? true : false;
30894     },
30895     
30896     setActivePanel : function(panel){
30897         panel = this.getPanel(panel);
30898         if(this.activePanel && this.activePanel != panel){
30899             this.activePanel.setActiveState(false);
30900             this.activePanel.getEl().setLeftTop(-10000,-10000);
30901         }
30902         this.activePanel = panel;
30903         panel.setActiveState(true);
30904         if(this.box){
30905             panel.setSize(this.box.width, this.box.height);
30906         }
30907         this.fireEvent("panelactivated", this, panel);
30908         this.fireEvent("invalidated");
30909     },
30910     
30911     /**
30912      * Show the specified panel.
30913      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
30914      * @return {Roo.ContentPanel} The shown panel or null
30915      */
30916     showPanel : function(panel){
30917         if(panel = this.getPanel(panel)){
30918             this.setActivePanel(panel);
30919         }
30920         return panel;
30921     },
30922     
30923     /**
30924      * Get the active panel for this region.
30925      * @return {Roo.ContentPanel} The active panel or null
30926      */
30927     getActivePanel : function(){
30928         return this.activePanel;
30929     },
30930     
30931     /**
30932      * Add the passed ContentPanel(s)
30933      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30934      * @return {Roo.ContentPanel} The panel added (if only one was added)
30935      */
30936     add : function(panel){
30937         if(arguments.length > 1){
30938             for(var i = 0, len = arguments.length; i < len; i++) {
30939                 this.add(arguments[i]);
30940             }
30941             return null;
30942         }
30943         if(this.hasPanel(panel)){
30944             this.showPanel(panel);
30945             return panel;
30946         }
30947         var el = panel.getEl();
30948         if(el.dom.parentNode != this.mgr.el.dom){
30949             this.mgr.el.dom.appendChild(el.dom);
30950         }
30951         if(panel.setRegion){
30952             panel.setRegion(this);
30953         }
30954         this.panels.add(panel);
30955         el.setStyle("position", "absolute");
30956         if(!panel.background){
30957             this.setActivePanel(panel);
30958             if(this.config.initialSize && this.panels.getCount()==1){
30959                 this.resizeTo(this.config.initialSize);
30960             }
30961         }
30962         this.fireEvent("paneladded", this, panel);
30963         return panel;
30964     },
30965     
30966     /**
30967      * Returns true if the panel is in this region.
30968      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30969      * @return {Boolean}
30970      */
30971     hasPanel : function(panel){
30972         if(typeof panel == "object"){ // must be panel obj
30973             panel = panel.getId();
30974         }
30975         return this.getPanel(panel) ? true : false;
30976     },
30977     
30978     /**
30979      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30980      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30981      * @param {Boolean} preservePanel Overrides the config preservePanel option
30982      * @return {Roo.ContentPanel} The panel that was removed
30983      */
30984     remove : function(panel, preservePanel){
30985         panel = this.getPanel(panel);
30986         if(!panel){
30987             return null;
30988         }
30989         var e = {};
30990         this.fireEvent("beforeremove", this, panel, e);
30991         if(e.cancel === true){
30992             return null;
30993         }
30994         var panelId = panel.getId();
30995         this.panels.removeKey(panelId);
30996         return panel;
30997     },
30998     
30999     /**
31000      * Returns the panel specified or null if it's not in this region.
31001      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31002      * @return {Roo.ContentPanel}
31003      */
31004     getPanel : function(id){
31005         if(typeof id == "object"){ // must be panel obj
31006             return id;
31007         }
31008         return this.panels.get(id);
31009     },
31010     
31011     /**
31012      * Returns this regions position (north/south/east/west/center).
31013      * @return {String} 
31014      */
31015     getPosition: function(){
31016         return this.position;    
31017     }
31018 });/*
31019  * Based on:
31020  * Ext JS Library 1.1.1
31021  * Copyright(c) 2006-2007, Ext JS, LLC.
31022  *
31023  * Originally Released Under LGPL - original licence link has changed is not relivant.
31024  *
31025  * Fork - LGPL
31026  * <script type="text/javascript">
31027  */
31028  
31029 /**
31030  * @class Roo.LayoutRegion
31031  * @extends Roo.BasicLayoutRegion
31032  * This class represents a region in a layout manager.
31033  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
31034  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
31035  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
31036  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31037  * @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})
31038  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
31039  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
31040  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
31041  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
31042  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
31043  * @cfg {String}    title           The title for the region (overrides panel titles)
31044  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
31045  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31046  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
31047  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31048  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
31049  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31050  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
31051  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
31052  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
31053  * @cfg {Boolean}   showPin         True to show a pin button
31054  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
31055  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
31056  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
31057  * @cfg {Number}    width           For East/West panels
31058  * @cfg {Number}    height          For North/South panels
31059  * @cfg {Boolean}   split           To show the splitter
31060  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
31061  */
31062 Roo.LayoutRegion = function(mgr, config, pos){
31063     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
31064     var dh = Roo.DomHelper;
31065     /** This region's container element 
31066     * @type Roo.Element */
31067     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
31068     /** This region's title element 
31069     * @type Roo.Element */
31070
31071     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
31072         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
31073         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
31074     ]}, true);
31075     this.titleEl.enableDisplayMode();
31076     /** This region's title text element 
31077     * @type HTMLElement */
31078     this.titleTextEl = this.titleEl.dom.firstChild;
31079     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
31080     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
31081     this.closeBtn.enableDisplayMode();
31082     this.closeBtn.on("click", this.closeClicked, this);
31083     this.closeBtn.hide();
31084
31085     this.createBody(config);
31086     this.visible = true;
31087     this.collapsed = false;
31088
31089     if(config.hideWhenEmpty){
31090         this.hide();
31091         this.on("paneladded", this.validateVisibility, this);
31092         this.on("panelremoved", this.validateVisibility, this);
31093     }
31094     this.applyConfig(config);
31095 };
31096
31097 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
31098
31099     createBody : function(){
31100         /** This region's body element 
31101         * @type Roo.Element */
31102         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
31103     },
31104
31105     applyConfig : function(c){
31106         if(c.collapsible && this.position != "center" && !this.collapsedEl){
31107             var dh = Roo.DomHelper;
31108             if(c.titlebar !== false){
31109                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
31110                 this.collapseBtn.on("click", this.collapse, this);
31111                 this.collapseBtn.enableDisplayMode();
31112
31113                 if(c.showPin === true || this.showPin){
31114                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
31115                     this.stickBtn.enableDisplayMode();
31116                     this.stickBtn.on("click", this.expand, this);
31117                     this.stickBtn.hide();
31118                 }
31119             }
31120             /** This region's collapsed element
31121             * @type Roo.Element */
31122             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
31123                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
31124             ]}, true);
31125             if(c.floatable !== false){
31126                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
31127                this.collapsedEl.on("click", this.collapseClick, this);
31128             }
31129
31130             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
31131                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
31132                    id: "message", unselectable: "on", style:{"float":"left"}});
31133                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
31134              }
31135             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
31136             this.expandBtn.on("click", this.expand, this);
31137         }
31138         if(this.collapseBtn){
31139             this.collapseBtn.setVisible(c.collapsible == true);
31140         }
31141         this.cmargins = c.cmargins || this.cmargins ||
31142                          (this.position == "west" || this.position == "east" ?
31143                              {top: 0, left: 2, right:2, bottom: 0} :
31144                              {top: 2, left: 0, right:0, bottom: 2});
31145         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31146         this.bottomTabs = c.tabPosition != "top";
31147         this.autoScroll = c.autoScroll || false;
31148         if(this.autoScroll){
31149             this.bodyEl.setStyle("overflow", "auto");
31150         }else{
31151             this.bodyEl.setStyle("overflow", "hidden");
31152         }
31153         //if(c.titlebar !== false){
31154             if((!c.titlebar && !c.title) || c.titlebar === false){
31155                 this.titleEl.hide();
31156             }else{
31157                 this.titleEl.show();
31158                 if(c.title){
31159                     this.titleTextEl.innerHTML = c.title;
31160                 }
31161             }
31162         //}
31163         this.duration = c.duration || .30;
31164         this.slideDuration = c.slideDuration || .45;
31165         this.config = c;
31166         if(c.collapsed){
31167             this.collapse(true);
31168         }
31169         if(c.hidden){
31170             this.hide();
31171         }
31172     },
31173     /**
31174      * Returns true if this region is currently visible.
31175      * @return {Boolean}
31176      */
31177     isVisible : function(){
31178         return this.visible;
31179     },
31180
31181     /**
31182      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
31183      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
31184      */
31185     setCollapsedTitle : function(title){
31186         title = title || "&#160;";
31187         if(this.collapsedTitleTextEl){
31188             this.collapsedTitleTextEl.innerHTML = title;
31189         }
31190     },
31191
31192     getBox : function(){
31193         var b;
31194         if(!this.collapsed){
31195             b = this.el.getBox(false, true);
31196         }else{
31197             b = this.collapsedEl.getBox(false, true);
31198         }
31199         return b;
31200     },
31201
31202     getMargins : function(){
31203         return this.collapsed ? this.cmargins : this.margins;
31204     },
31205
31206     highlight : function(){
31207         this.el.addClass("x-layout-panel-dragover");
31208     },
31209
31210     unhighlight : function(){
31211         this.el.removeClass("x-layout-panel-dragover");
31212     },
31213
31214     updateBox : function(box){
31215         this.box = box;
31216         if(!this.collapsed){
31217             this.el.dom.style.left = box.x + "px";
31218             this.el.dom.style.top = box.y + "px";
31219             this.updateBody(box.width, box.height);
31220         }else{
31221             this.collapsedEl.dom.style.left = box.x + "px";
31222             this.collapsedEl.dom.style.top = box.y + "px";
31223             this.collapsedEl.setSize(box.width, box.height);
31224         }
31225         if(this.tabs){
31226             this.tabs.autoSizeTabs();
31227         }
31228     },
31229
31230     updateBody : function(w, h){
31231         if(w !== null){
31232             this.el.setWidth(w);
31233             w -= this.el.getBorderWidth("rl");
31234             if(this.config.adjustments){
31235                 w += this.config.adjustments[0];
31236             }
31237         }
31238         if(h !== null){
31239             this.el.setHeight(h);
31240             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
31241             h -= this.el.getBorderWidth("tb");
31242             if(this.config.adjustments){
31243                 h += this.config.adjustments[1];
31244             }
31245             this.bodyEl.setHeight(h);
31246             if(this.tabs){
31247                 h = this.tabs.syncHeight(h);
31248             }
31249         }
31250         if(this.panelSize){
31251             w = w !== null ? w : this.panelSize.width;
31252             h = h !== null ? h : this.panelSize.height;
31253         }
31254         if(this.activePanel){
31255             var el = this.activePanel.getEl();
31256             w = w !== null ? w : el.getWidth();
31257             h = h !== null ? h : el.getHeight();
31258             this.panelSize = {width: w, height: h};
31259             this.activePanel.setSize(w, h);
31260         }
31261         if(Roo.isIE && this.tabs){
31262             this.tabs.el.repaint();
31263         }
31264     },
31265
31266     /**
31267      * Returns the container element for this region.
31268      * @return {Roo.Element}
31269      */
31270     getEl : function(){
31271         return this.el;
31272     },
31273
31274     /**
31275      * Hides this region.
31276      */
31277     hide : function(){
31278         if(!this.collapsed){
31279             this.el.dom.style.left = "-2000px";
31280             this.el.hide();
31281         }else{
31282             this.collapsedEl.dom.style.left = "-2000px";
31283             this.collapsedEl.hide();
31284         }
31285         this.visible = false;
31286         this.fireEvent("visibilitychange", this, false);
31287     },
31288
31289     /**
31290      * Shows this region if it was previously hidden.
31291      */
31292     show : function(){
31293         if(!this.collapsed){
31294             this.el.show();
31295         }else{
31296             this.collapsedEl.show();
31297         }
31298         this.visible = true;
31299         this.fireEvent("visibilitychange", this, true);
31300     },
31301
31302     closeClicked : function(){
31303         if(this.activePanel){
31304             this.remove(this.activePanel);
31305         }
31306     },
31307
31308     collapseClick : function(e){
31309         if(this.isSlid){
31310            e.stopPropagation();
31311            this.slideIn();
31312         }else{
31313            e.stopPropagation();
31314            this.slideOut();
31315         }
31316     },
31317
31318     /**
31319      * Collapses this region.
31320      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
31321      */
31322     collapse : function(skipAnim){
31323         if(this.collapsed) return;
31324         this.collapsed = true;
31325         if(this.split){
31326             this.split.el.hide();
31327         }
31328         if(this.config.animate && skipAnim !== true){
31329             this.fireEvent("invalidated", this);
31330             this.animateCollapse();
31331         }else{
31332             this.el.setLocation(-20000,-20000);
31333             this.el.hide();
31334             this.collapsedEl.show();
31335             this.fireEvent("collapsed", this);
31336             this.fireEvent("invalidated", this);
31337         }
31338     },
31339
31340     animateCollapse : function(){
31341         // overridden
31342     },
31343
31344     /**
31345      * Expands this region if it was previously collapsed.
31346      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
31347      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
31348      */
31349     expand : function(e, skipAnim){
31350         if(e) e.stopPropagation();
31351         if(!this.collapsed || this.el.hasActiveFx()) return;
31352         if(this.isSlid){
31353             this.afterSlideIn();
31354             skipAnim = true;
31355         }
31356         this.collapsed = false;
31357         if(this.config.animate && skipAnim !== true){
31358             this.animateExpand();
31359         }else{
31360             this.el.show();
31361             if(this.split){
31362                 this.split.el.show();
31363             }
31364             this.collapsedEl.setLocation(-2000,-2000);
31365             this.collapsedEl.hide();
31366             this.fireEvent("invalidated", this);
31367             this.fireEvent("expanded", this);
31368         }
31369     },
31370
31371     animateExpand : function(){
31372         // overridden
31373     },
31374
31375     initTabs : function()
31376     {
31377         this.bodyEl.setStyle("overflow", "hidden");
31378         var ts = new Roo.TabPanel(
31379                 this.bodyEl.dom,
31380                 {
31381                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
31382                     disableTooltips: this.config.disableTabTips,
31383                     toolbar : this.config.toolbar
31384                 }
31385         );
31386         if(this.config.hideTabs){
31387             ts.stripWrap.setDisplayed(false);
31388         }
31389         this.tabs = ts;
31390         ts.resizeTabs = this.config.resizeTabs === true;
31391         ts.minTabWidth = this.config.minTabWidth || 40;
31392         ts.maxTabWidth = this.config.maxTabWidth || 250;
31393         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
31394         ts.monitorResize = false;
31395         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31396         ts.bodyEl.addClass('x-layout-tabs-body');
31397         this.panels.each(this.initPanelAsTab, this);
31398     },
31399
31400     initPanelAsTab : function(panel){
31401         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
31402                     this.config.closeOnTab && panel.isClosable());
31403         if(panel.tabTip !== undefined){
31404             ti.setTooltip(panel.tabTip);
31405         }
31406         ti.on("activate", function(){
31407               this.setActivePanel(panel);
31408         }, this);
31409         if(this.config.closeOnTab){
31410             ti.on("beforeclose", function(t, e){
31411                 e.cancel = true;
31412                 this.remove(panel);
31413             }, this);
31414         }
31415         return ti;
31416     },
31417
31418     updatePanelTitle : function(panel, title){
31419         if(this.activePanel == panel){
31420             this.updateTitle(title);
31421         }
31422         if(this.tabs){
31423             var ti = this.tabs.getTab(panel.getEl().id);
31424             ti.setText(title);
31425             if(panel.tabTip !== undefined){
31426                 ti.setTooltip(panel.tabTip);
31427             }
31428         }
31429     },
31430
31431     updateTitle : function(title){
31432         if(this.titleTextEl && !this.config.title){
31433             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
31434         }
31435     },
31436
31437     setActivePanel : function(panel){
31438         panel = this.getPanel(panel);
31439         if(this.activePanel && this.activePanel != panel){
31440             this.activePanel.setActiveState(false);
31441         }
31442         this.activePanel = panel;
31443         panel.setActiveState(true);
31444         if(this.panelSize){
31445             panel.setSize(this.panelSize.width, this.panelSize.height);
31446         }
31447         if(this.closeBtn){
31448             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
31449         }
31450         this.updateTitle(panel.getTitle());
31451         if(this.tabs){
31452             this.fireEvent("invalidated", this);
31453         }
31454         this.fireEvent("panelactivated", this, panel);
31455     },
31456
31457     /**
31458      * Shows the specified panel.
31459      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
31460      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
31461      */
31462     showPanel : function(panel){
31463         if(panel = this.getPanel(panel)){
31464             if(this.tabs){
31465                 var tab = this.tabs.getTab(panel.getEl().id);
31466                 if(tab.isHidden()){
31467                     this.tabs.unhideTab(tab.id);
31468                 }
31469                 tab.activate();
31470             }else{
31471                 this.setActivePanel(panel);
31472             }
31473         }
31474         return panel;
31475     },
31476
31477     /**
31478      * Get the active panel for this region.
31479      * @return {Roo.ContentPanel} The active panel or null
31480      */
31481     getActivePanel : function(){
31482         return this.activePanel;
31483     },
31484
31485     validateVisibility : function(){
31486         if(this.panels.getCount() < 1){
31487             this.updateTitle("&#160;");
31488             this.closeBtn.hide();
31489             this.hide();
31490         }else{
31491             if(!this.isVisible()){
31492                 this.show();
31493             }
31494         }
31495     },
31496
31497     /**
31498      * Adds the passed ContentPanel(s) to this region.
31499      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31500      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
31501      */
31502     add : function(panel){
31503         if(arguments.length > 1){
31504             for(var i = 0, len = arguments.length; i < len; i++) {
31505                 this.add(arguments[i]);
31506             }
31507             return null;
31508         }
31509         if(this.hasPanel(panel)){
31510             this.showPanel(panel);
31511             return panel;
31512         }
31513         panel.setRegion(this);
31514         this.panels.add(panel);
31515         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
31516             this.bodyEl.dom.appendChild(panel.getEl().dom);
31517             if(panel.background !== true){
31518                 this.setActivePanel(panel);
31519             }
31520             this.fireEvent("paneladded", this, panel);
31521             return panel;
31522         }
31523         if(!this.tabs){
31524             this.initTabs();
31525         }else{
31526             this.initPanelAsTab(panel);
31527         }
31528         if(panel.background !== true){
31529             this.tabs.activate(panel.getEl().id);
31530         }
31531         this.fireEvent("paneladded", this, panel);
31532         return panel;
31533     },
31534
31535     /**
31536      * Hides the tab for the specified panel.
31537      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31538      */
31539     hidePanel : function(panel){
31540         if(this.tabs && (panel = this.getPanel(panel))){
31541             this.tabs.hideTab(panel.getEl().id);
31542         }
31543     },
31544
31545     /**
31546      * Unhides the tab for a previously hidden panel.
31547      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31548      */
31549     unhidePanel : function(panel){
31550         if(this.tabs && (panel = this.getPanel(panel))){
31551             this.tabs.unhideTab(panel.getEl().id);
31552         }
31553     },
31554
31555     clearPanels : function(){
31556         while(this.panels.getCount() > 0){
31557              this.remove(this.panels.first());
31558         }
31559     },
31560
31561     /**
31562      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31563      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31564      * @param {Boolean} preservePanel Overrides the config preservePanel option
31565      * @return {Roo.ContentPanel} The panel that was removed
31566      */
31567     remove : function(panel, preservePanel){
31568         panel = this.getPanel(panel);
31569         if(!panel){
31570             return null;
31571         }
31572         var e = {};
31573         this.fireEvent("beforeremove", this, panel, e);
31574         if(e.cancel === true){
31575             return null;
31576         }
31577         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
31578         var panelId = panel.getId();
31579         this.panels.removeKey(panelId);
31580         if(preservePanel){
31581             document.body.appendChild(panel.getEl().dom);
31582         }
31583         if(this.tabs){
31584             this.tabs.removeTab(panel.getEl().id);
31585         }else if (!preservePanel){
31586             this.bodyEl.dom.removeChild(panel.getEl().dom);
31587         }
31588         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
31589             var p = this.panels.first();
31590             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
31591             tempEl.appendChild(p.getEl().dom);
31592             this.bodyEl.update("");
31593             this.bodyEl.dom.appendChild(p.getEl().dom);
31594             tempEl = null;
31595             this.updateTitle(p.getTitle());
31596             this.tabs = null;
31597             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31598             this.setActivePanel(p);
31599         }
31600         panel.setRegion(null);
31601         if(this.activePanel == panel){
31602             this.activePanel = null;
31603         }
31604         if(this.config.autoDestroy !== false && preservePanel !== true){
31605             try{panel.destroy();}catch(e){}
31606         }
31607         this.fireEvent("panelremoved", this, panel);
31608         return panel;
31609     },
31610
31611     /**
31612      * Returns the TabPanel component used by this region
31613      * @return {Roo.TabPanel}
31614      */
31615     getTabs : function(){
31616         return this.tabs;
31617     },
31618
31619     createTool : function(parentEl, className){
31620         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
31621             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
31622         btn.addClassOnOver("x-layout-tools-button-over");
31623         return btn;
31624     }
31625 });/*
31626  * Based on:
31627  * Ext JS Library 1.1.1
31628  * Copyright(c) 2006-2007, Ext JS, LLC.
31629  *
31630  * Originally Released Under LGPL - original licence link has changed is not relivant.
31631  *
31632  * Fork - LGPL
31633  * <script type="text/javascript">
31634  */
31635  
31636
31637
31638 /**
31639  * @class Roo.SplitLayoutRegion
31640  * @extends Roo.LayoutRegion
31641  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
31642  */
31643 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
31644     this.cursor = cursor;
31645     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
31646 };
31647
31648 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
31649     splitTip : "Drag to resize.",
31650     collapsibleSplitTip : "Drag to resize. Double click to hide.",
31651     useSplitTips : false,
31652
31653     applyConfig : function(config){
31654         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
31655         if(config.split){
31656             if(!this.split){
31657                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
31658                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
31659                 /** The SplitBar for this region 
31660                 * @type Roo.SplitBar */
31661                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
31662                 this.split.on("moved", this.onSplitMove, this);
31663                 this.split.useShim = config.useShim === true;
31664                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
31665                 if(this.useSplitTips){
31666                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
31667                 }
31668                 if(config.collapsible){
31669                     this.split.el.on("dblclick", this.collapse,  this);
31670                 }
31671             }
31672             if(typeof config.minSize != "undefined"){
31673                 this.split.minSize = config.minSize;
31674             }
31675             if(typeof config.maxSize != "undefined"){
31676                 this.split.maxSize = config.maxSize;
31677             }
31678             if(config.hideWhenEmpty || config.hidden || config.collapsed){
31679                 this.hideSplitter();
31680             }
31681         }
31682     },
31683
31684     getHMaxSize : function(){
31685          var cmax = this.config.maxSize || 10000;
31686          var center = this.mgr.getRegion("center");
31687          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
31688     },
31689
31690     getVMaxSize : function(){
31691          var cmax = this.config.maxSize || 10000;
31692          var center = this.mgr.getRegion("center");
31693          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
31694     },
31695
31696     onSplitMove : function(split, newSize){
31697         this.fireEvent("resized", this, newSize);
31698     },
31699     
31700     /** 
31701      * Returns the {@link Roo.SplitBar} for this region.
31702      * @return {Roo.SplitBar}
31703      */
31704     getSplitBar : function(){
31705         return this.split;
31706     },
31707     
31708     hide : function(){
31709         this.hideSplitter();
31710         Roo.SplitLayoutRegion.superclass.hide.call(this);
31711     },
31712
31713     hideSplitter : function(){
31714         if(this.split){
31715             this.split.el.setLocation(-2000,-2000);
31716             this.split.el.hide();
31717         }
31718     },
31719
31720     show : function(){
31721         if(this.split){
31722             this.split.el.show();
31723         }
31724         Roo.SplitLayoutRegion.superclass.show.call(this);
31725     },
31726     
31727     beforeSlide: function(){
31728         if(Roo.isGecko){// firefox overflow auto bug workaround
31729             this.bodyEl.clip();
31730             if(this.tabs) this.tabs.bodyEl.clip();
31731             if(this.activePanel){
31732                 this.activePanel.getEl().clip();
31733                 
31734                 if(this.activePanel.beforeSlide){
31735                     this.activePanel.beforeSlide();
31736                 }
31737             }
31738         }
31739     },
31740     
31741     afterSlide : function(){
31742         if(Roo.isGecko){// firefox overflow auto bug workaround
31743             this.bodyEl.unclip();
31744             if(this.tabs) this.tabs.bodyEl.unclip();
31745             if(this.activePanel){
31746                 this.activePanel.getEl().unclip();
31747                 if(this.activePanel.afterSlide){
31748                     this.activePanel.afterSlide();
31749                 }
31750             }
31751         }
31752     },
31753
31754     initAutoHide : function(){
31755         if(this.autoHide !== false){
31756             if(!this.autoHideHd){
31757                 var st = new Roo.util.DelayedTask(this.slideIn, this);
31758                 this.autoHideHd = {
31759                     "mouseout": function(e){
31760                         if(!e.within(this.el, true)){
31761                             st.delay(500);
31762                         }
31763                     },
31764                     "mouseover" : function(e){
31765                         st.cancel();
31766                     },
31767                     scope : this
31768                 };
31769             }
31770             this.el.on(this.autoHideHd);
31771         }
31772     },
31773
31774     clearAutoHide : function(){
31775         if(this.autoHide !== false){
31776             this.el.un("mouseout", this.autoHideHd.mouseout);
31777             this.el.un("mouseover", this.autoHideHd.mouseover);
31778         }
31779     },
31780
31781     clearMonitor : function(){
31782         Roo.get(document).un("click", this.slideInIf, this);
31783     },
31784
31785     // these names are backwards but not changed for compat
31786     slideOut : function(){
31787         if(this.isSlid || this.el.hasActiveFx()){
31788             return;
31789         }
31790         this.isSlid = true;
31791         if(this.collapseBtn){
31792             this.collapseBtn.hide();
31793         }
31794         this.closeBtnState = this.closeBtn.getStyle('display');
31795         this.closeBtn.hide();
31796         if(this.stickBtn){
31797             this.stickBtn.show();
31798         }
31799         this.el.show();
31800         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
31801         this.beforeSlide();
31802         this.el.setStyle("z-index", 10001);
31803         this.el.slideIn(this.getSlideAnchor(), {
31804             callback: function(){
31805                 this.afterSlide();
31806                 this.initAutoHide();
31807                 Roo.get(document).on("click", this.slideInIf, this);
31808                 this.fireEvent("slideshow", this);
31809             },
31810             scope: this,
31811             block: true
31812         });
31813     },
31814
31815     afterSlideIn : function(){
31816         this.clearAutoHide();
31817         this.isSlid = false;
31818         this.clearMonitor();
31819         this.el.setStyle("z-index", "");
31820         if(this.collapseBtn){
31821             this.collapseBtn.show();
31822         }
31823         this.closeBtn.setStyle('display', this.closeBtnState);
31824         if(this.stickBtn){
31825             this.stickBtn.hide();
31826         }
31827         this.fireEvent("slidehide", this);
31828     },
31829
31830     slideIn : function(cb){
31831         if(!this.isSlid || this.el.hasActiveFx()){
31832             Roo.callback(cb);
31833             return;
31834         }
31835         this.isSlid = false;
31836         this.beforeSlide();
31837         this.el.slideOut(this.getSlideAnchor(), {
31838             callback: function(){
31839                 this.el.setLeftTop(-10000, -10000);
31840                 this.afterSlide();
31841                 this.afterSlideIn();
31842                 Roo.callback(cb);
31843             },
31844             scope: this,
31845             block: true
31846         });
31847     },
31848     
31849     slideInIf : function(e){
31850         if(!e.within(this.el)){
31851             this.slideIn();
31852         }
31853     },
31854
31855     animateCollapse : function(){
31856         this.beforeSlide();
31857         this.el.setStyle("z-index", 20000);
31858         var anchor = this.getSlideAnchor();
31859         this.el.slideOut(anchor, {
31860             callback : function(){
31861                 this.el.setStyle("z-index", "");
31862                 this.collapsedEl.slideIn(anchor, {duration:.3});
31863                 this.afterSlide();
31864                 this.el.setLocation(-10000,-10000);
31865                 this.el.hide();
31866                 this.fireEvent("collapsed", this);
31867             },
31868             scope: this,
31869             block: true
31870         });
31871     },
31872
31873     animateExpand : function(){
31874         this.beforeSlide();
31875         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
31876         this.el.setStyle("z-index", 20000);
31877         this.collapsedEl.hide({
31878             duration:.1
31879         });
31880         this.el.slideIn(this.getSlideAnchor(), {
31881             callback : function(){
31882                 this.el.setStyle("z-index", "");
31883                 this.afterSlide();
31884                 if(this.split){
31885                     this.split.el.show();
31886                 }
31887                 this.fireEvent("invalidated", this);
31888                 this.fireEvent("expanded", this);
31889             },
31890             scope: this,
31891             block: true
31892         });
31893     },
31894
31895     anchors : {
31896         "west" : "left",
31897         "east" : "right",
31898         "north" : "top",
31899         "south" : "bottom"
31900     },
31901
31902     sanchors : {
31903         "west" : "l",
31904         "east" : "r",
31905         "north" : "t",
31906         "south" : "b"
31907     },
31908
31909     canchors : {
31910         "west" : "tl-tr",
31911         "east" : "tr-tl",
31912         "north" : "tl-bl",
31913         "south" : "bl-tl"
31914     },
31915
31916     getAnchor : function(){
31917         return this.anchors[this.position];
31918     },
31919
31920     getCollapseAnchor : function(){
31921         return this.canchors[this.position];
31922     },
31923
31924     getSlideAnchor : function(){
31925         return this.sanchors[this.position];
31926     },
31927
31928     getAlignAdj : function(){
31929         var cm = this.cmargins;
31930         switch(this.position){
31931             case "west":
31932                 return [0, 0];
31933             break;
31934             case "east":
31935                 return [0, 0];
31936             break;
31937             case "north":
31938                 return [0, 0];
31939             break;
31940             case "south":
31941                 return [0, 0];
31942             break;
31943         }
31944     },
31945
31946     getExpandAdj : function(){
31947         var c = this.collapsedEl, cm = this.cmargins;
31948         switch(this.position){
31949             case "west":
31950                 return [-(cm.right+c.getWidth()+cm.left), 0];
31951             break;
31952             case "east":
31953                 return [cm.right+c.getWidth()+cm.left, 0];
31954             break;
31955             case "north":
31956                 return [0, -(cm.top+cm.bottom+c.getHeight())];
31957             break;
31958             case "south":
31959                 return [0, cm.top+cm.bottom+c.getHeight()];
31960             break;
31961         }
31962     }
31963 });/*
31964  * Based on:
31965  * Ext JS Library 1.1.1
31966  * Copyright(c) 2006-2007, Ext JS, LLC.
31967  *
31968  * Originally Released Under LGPL - original licence link has changed is not relivant.
31969  *
31970  * Fork - LGPL
31971  * <script type="text/javascript">
31972  */
31973 /*
31974  * These classes are private internal classes
31975  */
31976 Roo.CenterLayoutRegion = function(mgr, config){
31977     Roo.LayoutRegion.call(this, mgr, config, "center");
31978     this.visible = true;
31979     this.minWidth = config.minWidth || 20;
31980     this.minHeight = config.minHeight || 20;
31981 };
31982
31983 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31984     hide : function(){
31985         // center panel can't be hidden
31986     },
31987     
31988     show : function(){
31989         // center panel can't be hidden
31990     },
31991     
31992     getMinWidth: function(){
31993         return this.minWidth;
31994     },
31995     
31996     getMinHeight: function(){
31997         return this.minHeight;
31998     }
31999 });
32000
32001
32002 Roo.NorthLayoutRegion = function(mgr, config){
32003     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
32004     if(this.split){
32005         this.split.placement = Roo.SplitBar.TOP;
32006         this.split.orientation = Roo.SplitBar.VERTICAL;
32007         this.split.el.addClass("x-layout-split-v");
32008     }
32009     var size = config.initialSize || config.height;
32010     if(typeof size != "undefined"){
32011         this.el.setHeight(size);
32012     }
32013 };
32014 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
32015     orientation: Roo.SplitBar.VERTICAL,
32016     getBox : function(){
32017         if(this.collapsed){
32018             return this.collapsedEl.getBox();
32019         }
32020         var box = this.el.getBox();
32021         if(this.split){
32022             box.height += this.split.el.getHeight();
32023         }
32024         return box;
32025     },
32026     
32027     updateBox : function(box){
32028         if(this.split && !this.collapsed){
32029             box.height -= this.split.el.getHeight();
32030             this.split.el.setLeft(box.x);
32031             this.split.el.setTop(box.y+box.height);
32032             this.split.el.setWidth(box.width);
32033         }
32034         if(this.collapsed){
32035             this.updateBody(box.width, null);
32036         }
32037         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32038     }
32039 });
32040
32041 Roo.SouthLayoutRegion = function(mgr, config){
32042     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
32043     if(this.split){
32044         this.split.placement = Roo.SplitBar.BOTTOM;
32045         this.split.orientation = Roo.SplitBar.VERTICAL;
32046         this.split.el.addClass("x-layout-split-v");
32047     }
32048     var size = config.initialSize || config.height;
32049     if(typeof size != "undefined"){
32050         this.el.setHeight(size);
32051     }
32052 };
32053 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
32054     orientation: Roo.SplitBar.VERTICAL,
32055     getBox : function(){
32056         if(this.collapsed){
32057             return this.collapsedEl.getBox();
32058         }
32059         var box = this.el.getBox();
32060         if(this.split){
32061             var sh = this.split.el.getHeight();
32062             box.height += sh;
32063             box.y -= sh;
32064         }
32065         return box;
32066     },
32067     
32068     updateBox : function(box){
32069         if(this.split && !this.collapsed){
32070             var sh = this.split.el.getHeight();
32071             box.height -= sh;
32072             box.y += sh;
32073             this.split.el.setLeft(box.x);
32074             this.split.el.setTop(box.y-sh);
32075             this.split.el.setWidth(box.width);
32076         }
32077         if(this.collapsed){
32078             this.updateBody(box.width, null);
32079         }
32080         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32081     }
32082 });
32083
32084 Roo.EastLayoutRegion = function(mgr, config){
32085     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
32086     if(this.split){
32087         this.split.placement = Roo.SplitBar.RIGHT;
32088         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32089         this.split.el.addClass("x-layout-split-h");
32090     }
32091     var size = config.initialSize || config.width;
32092     if(typeof size != "undefined"){
32093         this.el.setWidth(size);
32094     }
32095 };
32096 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
32097     orientation: Roo.SplitBar.HORIZONTAL,
32098     getBox : function(){
32099         if(this.collapsed){
32100             return this.collapsedEl.getBox();
32101         }
32102         var box = this.el.getBox();
32103         if(this.split){
32104             var sw = this.split.el.getWidth();
32105             box.width += sw;
32106             box.x -= sw;
32107         }
32108         return box;
32109     },
32110
32111     updateBox : function(box){
32112         if(this.split && !this.collapsed){
32113             var sw = this.split.el.getWidth();
32114             box.width -= sw;
32115             this.split.el.setLeft(box.x);
32116             this.split.el.setTop(box.y);
32117             this.split.el.setHeight(box.height);
32118             box.x += sw;
32119         }
32120         if(this.collapsed){
32121             this.updateBody(null, box.height);
32122         }
32123         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32124     }
32125 });
32126
32127 Roo.WestLayoutRegion = function(mgr, config){
32128     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
32129     if(this.split){
32130         this.split.placement = Roo.SplitBar.LEFT;
32131         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32132         this.split.el.addClass("x-layout-split-h");
32133     }
32134     var size = config.initialSize || config.width;
32135     if(typeof size != "undefined"){
32136         this.el.setWidth(size);
32137     }
32138 };
32139 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
32140     orientation: Roo.SplitBar.HORIZONTAL,
32141     getBox : function(){
32142         if(this.collapsed){
32143             return this.collapsedEl.getBox();
32144         }
32145         var box = this.el.getBox();
32146         if(this.split){
32147             box.width += this.split.el.getWidth();
32148         }
32149         return box;
32150     },
32151     
32152     updateBox : function(box){
32153         if(this.split && !this.collapsed){
32154             var sw = this.split.el.getWidth();
32155             box.width -= sw;
32156             this.split.el.setLeft(box.x+box.width);
32157             this.split.el.setTop(box.y);
32158             this.split.el.setHeight(box.height);
32159         }
32160         if(this.collapsed){
32161             this.updateBody(null, box.height);
32162         }
32163         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32164     }
32165 });
32166 /*
32167  * Based on:
32168  * Ext JS Library 1.1.1
32169  * Copyright(c) 2006-2007, Ext JS, LLC.
32170  *
32171  * Originally Released Under LGPL - original licence link has changed is not relivant.
32172  *
32173  * Fork - LGPL
32174  * <script type="text/javascript">
32175  */
32176  
32177  
32178 /*
32179  * Private internal class for reading and applying state
32180  */
32181 Roo.LayoutStateManager = function(layout){
32182      // default empty state
32183      this.state = {
32184         north: {},
32185         south: {},
32186         east: {},
32187         west: {}       
32188     };
32189 };
32190
32191 Roo.LayoutStateManager.prototype = {
32192     init : function(layout, provider){
32193         this.provider = provider;
32194         var state = provider.get(layout.id+"-layout-state");
32195         if(state){
32196             var wasUpdating = layout.isUpdating();
32197             if(!wasUpdating){
32198                 layout.beginUpdate();
32199             }
32200             for(var key in state){
32201                 if(typeof state[key] != "function"){
32202                     var rstate = state[key];
32203                     var r = layout.getRegion(key);
32204                     if(r && rstate){
32205                         if(rstate.size){
32206                             r.resizeTo(rstate.size);
32207                         }
32208                         if(rstate.collapsed == true){
32209                             r.collapse(true);
32210                         }else{
32211                             r.expand(null, true);
32212                         }
32213                     }
32214                 }
32215             }
32216             if(!wasUpdating){
32217                 layout.endUpdate();
32218             }
32219             this.state = state; 
32220         }
32221         this.layout = layout;
32222         layout.on("regionresized", this.onRegionResized, this);
32223         layout.on("regioncollapsed", this.onRegionCollapsed, this);
32224         layout.on("regionexpanded", this.onRegionExpanded, this);
32225     },
32226     
32227     storeState : function(){
32228         this.provider.set(this.layout.id+"-layout-state", this.state);
32229     },
32230     
32231     onRegionResized : function(region, newSize){
32232         this.state[region.getPosition()].size = newSize;
32233         this.storeState();
32234     },
32235     
32236     onRegionCollapsed : function(region){
32237         this.state[region.getPosition()].collapsed = true;
32238         this.storeState();
32239     },
32240     
32241     onRegionExpanded : function(region){
32242         this.state[region.getPosition()].collapsed = false;
32243         this.storeState();
32244     }
32245 };/*
32246  * Based on:
32247  * Ext JS Library 1.1.1
32248  * Copyright(c) 2006-2007, Ext JS, LLC.
32249  *
32250  * Originally Released Under LGPL - original licence link has changed is not relivant.
32251  *
32252  * Fork - LGPL
32253  * <script type="text/javascript">
32254  */
32255 /**
32256  * @class Roo.ContentPanel
32257  * @extends Roo.util.Observable
32258  * A basic ContentPanel element.
32259  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
32260  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
32261  * @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
32262  * @cfg {Boolean}   closable      True if the panel can be closed/removed
32263  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
32264  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
32265  * @cfg {Toolbar}   toolbar       A toolbar for this panel
32266  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
32267  * @cfg {String} title          The title for this panel
32268  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
32269  * @cfg {String} url            Calls {@link #setUrl} with this value
32270  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
32271  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
32272  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
32273  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
32274
32275  * @constructor
32276  * Create a new ContentPanel.
32277  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
32278  * @param {String/Object} config A string to set only the title or a config object
32279  * @param {String} content (optional) Set the HTML content for this panel
32280  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
32281  */
32282 Roo.ContentPanel = function(el, config, content){
32283     
32284      
32285     /*
32286     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
32287         config = el;
32288         el = Roo.id();
32289     }
32290     if (config && config.parentLayout) { 
32291         el = config.parentLayout.el.createChild(); 
32292     }
32293     */
32294     if(el.autoCreate){ // xtype is available if this is called from factory
32295         config = el;
32296         el = Roo.id();
32297     }
32298     this.el = Roo.get(el);
32299     if(!this.el && config && config.autoCreate){
32300         if(typeof config.autoCreate == "object"){
32301             if(!config.autoCreate.id){
32302                 config.autoCreate.id = config.id||el;
32303             }
32304             this.el = Roo.DomHelper.append(document.body,
32305                         config.autoCreate, true);
32306         }else{
32307             this.el = Roo.DomHelper.append(document.body,
32308                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
32309         }
32310     }
32311     this.closable = false;
32312     this.loaded = false;
32313     this.active = false;
32314     if(typeof config == "string"){
32315         this.title = config;
32316     }else{
32317         Roo.apply(this, config);
32318     }
32319     
32320     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
32321         this.wrapEl = this.el.wrap();
32322         this.toolbar.container = this.el.insertSibling(false, 'before');
32323         this.toolbar = new Roo.Toolbar(this.toolbar);
32324     }
32325     
32326     
32327     
32328     if(this.resizeEl){
32329         this.resizeEl = Roo.get(this.resizeEl, true);
32330     }else{
32331         this.resizeEl = this.el;
32332     }
32333     this.addEvents({
32334         /**
32335          * @event activate
32336          * Fires when this panel is activated. 
32337          * @param {Roo.ContentPanel} this
32338          */
32339         "activate" : true,
32340         /**
32341          * @event deactivate
32342          * Fires when this panel is activated. 
32343          * @param {Roo.ContentPanel} this
32344          */
32345         "deactivate" : true,
32346
32347         /**
32348          * @event resize
32349          * Fires when this panel is resized if fitToFrame is true.
32350          * @param {Roo.ContentPanel} this
32351          * @param {Number} width The width after any component adjustments
32352          * @param {Number} height The height after any component adjustments
32353          */
32354         "resize" : true,
32355         
32356          /**
32357          * @event render
32358          * Fires when this tab is created
32359          * @param {Roo.ContentPanel} this
32360          */
32361         "render" : true
32362         
32363         
32364         
32365     });
32366     if(this.autoScroll){
32367         this.resizeEl.setStyle("overflow", "auto");
32368     } else {
32369         // fix randome scrolling
32370         this.el.on('scroll', function() {
32371             Roo.log('fix random scolling');
32372             this.scrollTo('top',0); 
32373         });
32374     }
32375     content = content || this.content;
32376     if(content){
32377         this.setContent(content);
32378     }
32379     if(config && config.url){
32380         this.setUrl(this.url, this.params, this.loadOnce);
32381     }
32382     
32383     
32384     
32385     Roo.ContentPanel.superclass.constructor.call(this);
32386     
32387     this.fireEvent('render', this);
32388 };
32389
32390 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
32391     tabTip:'',
32392     setRegion : function(region){
32393         this.region = region;
32394         if(region){
32395            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
32396         }else{
32397            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
32398         } 
32399     },
32400     
32401     /**
32402      * Returns the toolbar for this Panel if one was configured. 
32403      * @return {Roo.Toolbar} 
32404      */
32405     getToolbar : function(){
32406         return this.toolbar;
32407     },
32408     
32409     setActiveState : function(active){
32410         this.active = active;
32411         if(!active){
32412             this.fireEvent("deactivate", this);
32413         }else{
32414             this.fireEvent("activate", this);
32415         }
32416     },
32417     /**
32418      * Updates this panel's element
32419      * @param {String} content The new content
32420      * @param {Boolean} loadScripts (optional) true to look for and process scripts
32421     */
32422     setContent : function(content, loadScripts){
32423         this.el.update(content, loadScripts);
32424     },
32425
32426     ignoreResize : function(w, h){
32427         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
32428             return true;
32429         }else{
32430             this.lastSize = {width: w, height: h};
32431             return false;
32432         }
32433     },
32434     /**
32435      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
32436      * @return {Roo.UpdateManager} The UpdateManager
32437      */
32438     getUpdateManager : function(){
32439         return this.el.getUpdateManager();
32440     },
32441      /**
32442      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
32443      * @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:
32444 <pre><code>
32445 panel.load({
32446     url: "your-url.php",
32447     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
32448     callback: yourFunction,
32449     scope: yourObject, //(optional scope)
32450     discardUrl: false,
32451     nocache: false,
32452     text: "Loading...",
32453     timeout: 30,
32454     scripts: false
32455 });
32456 </code></pre>
32457      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
32458      * 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.
32459      * @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}
32460      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
32461      * @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.
32462      * @return {Roo.ContentPanel} this
32463      */
32464     load : function(){
32465         var um = this.el.getUpdateManager();
32466         um.update.apply(um, arguments);
32467         return this;
32468     },
32469
32470
32471     /**
32472      * 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.
32473      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
32474      * @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)
32475      * @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)
32476      * @return {Roo.UpdateManager} The UpdateManager
32477      */
32478     setUrl : function(url, params, loadOnce){
32479         if(this.refreshDelegate){
32480             this.removeListener("activate", this.refreshDelegate);
32481         }
32482         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
32483         this.on("activate", this.refreshDelegate);
32484         return this.el.getUpdateManager();
32485     },
32486     
32487     _handleRefresh : function(url, params, loadOnce){
32488         if(!loadOnce || !this.loaded){
32489             var updater = this.el.getUpdateManager();
32490             updater.update(url, params, this._setLoaded.createDelegate(this));
32491         }
32492     },
32493     
32494     _setLoaded : function(){
32495         this.loaded = true;
32496     }, 
32497     
32498     /**
32499      * Returns this panel's id
32500      * @return {String} 
32501      */
32502     getId : function(){
32503         return this.el.id;
32504     },
32505     
32506     /** 
32507      * Returns this panel's element - used by regiosn to add.
32508      * @return {Roo.Element} 
32509      */
32510     getEl : function(){
32511         return this.wrapEl || this.el;
32512     },
32513     
32514     adjustForComponents : function(width, height){
32515         if(this.resizeEl != this.el){
32516             width -= this.el.getFrameWidth('lr');
32517             height -= this.el.getFrameWidth('tb');
32518         }
32519         if(this.toolbar){
32520             var te = this.toolbar.getEl();
32521             height -= te.getHeight();
32522             te.setWidth(width);
32523         }
32524         if(this.adjustments){
32525             width += this.adjustments[0];
32526             height += this.adjustments[1];
32527         }
32528         return {"width": width, "height": height};
32529     },
32530     
32531     setSize : function(width, height){
32532         if(this.fitToFrame && !this.ignoreResize(width, height)){
32533             if(this.fitContainer && this.resizeEl != this.el){
32534                 this.el.setSize(width, height);
32535             }
32536             var size = this.adjustForComponents(width, height);
32537             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
32538             this.fireEvent('resize', this, size.width, size.height);
32539         }
32540     },
32541     
32542     /**
32543      * Returns this panel's title
32544      * @return {String} 
32545      */
32546     getTitle : function(){
32547         return this.title;
32548     },
32549     
32550     /**
32551      * Set this panel's title
32552      * @param {String} title
32553      */
32554     setTitle : function(title){
32555         this.title = title;
32556         if(this.region){
32557             this.region.updatePanelTitle(this, title);
32558         }
32559     },
32560     
32561     /**
32562      * Returns true is this panel was configured to be closable
32563      * @return {Boolean} 
32564      */
32565     isClosable : function(){
32566         return this.closable;
32567     },
32568     
32569     beforeSlide : function(){
32570         this.el.clip();
32571         this.resizeEl.clip();
32572     },
32573     
32574     afterSlide : function(){
32575         this.el.unclip();
32576         this.resizeEl.unclip();
32577     },
32578     
32579     /**
32580      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
32581      *   Will fail silently if the {@link #setUrl} method has not been called.
32582      *   This does not activate the panel, just updates its content.
32583      */
32584     refresh : function(){
32585         if(this.refreshDelegate){
32586            this.loaded = false;
32587            this.refreshDelegate();
32588         }
32589     },
32590     
32591     /**
32592      * Destroys this panel
32593      */
32594     destroy : function(){
32595         this.el.removeAllListeners();
32596         var tempEl = document.createElement("span");
32597         tempEl.appendChild(this.el.dom);
32598         tempEl.innerHTML = "";
32599         this.el.remove();
32600         this.el = null;
32601     },
32602     
32603     /**
32604      * form - if the content panel contains a form - this is a reference to it.
32605      * @type {Roo.form.Form}
32606      */
32607     form : false,
32608     /**
32609      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
32610      *    This contains a reference to it.
32611      * @type {Roo.View}
32612      */
32613     view : false,
32614     
32615       /**
32616      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
32617      * <pre><code>
32618
32619 layout.addxtype({
32620        xtype : 'Form',
32621        items: [ .... ]
32622    }
32623 );
32624
32625 </code></pre>
32626      * @param {Object} cfg Xtype definition of item to add.
32627      */
32628     
32629     addxtype : function(cfg) {
32630         // add form..
32631         if (cfg.xtype.match(/^Form$/)) {
32632             var el = this.el.createChild();
32633
32634             this.form = new  Roo.form.Form(cfg);
32635             
32636             
32637             if ( this.form.allItems.length) this.form.render(el.dom);
32638             return this.form;
32639         }
32640         // should only have one of theses..
32641         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
32642             // views..
32643             cfg.el = this.el.appendChild(document.createElement("div"));
32644             // factory?
32645             
32646             var ret = new Roo.factory(cfg);
32647             ret.render && ret.render(false, ''); // render blank..
32648             this.view = ret;
32649             return ret;
32650         }
32651         return false;
32652     }
32653 });
32654
32655 /**
32656  * @class Roo.GridPanel
32657  * @extends Roo.ContentPanel
32658  * @constructor
32659  * Create a new GridPanel.
32660  * @param {Roo.grid.Grid} grid The grid for this panel
32661  * @param {String/Object} config A string to set only the panel's title, or a config object
32662  */
32663 Roo.GridPanel = function(grid, config){
32664     
32665   
32666     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
32667         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
32668         
32669     this.wrapper.dom.appendChild(grid.getGridEl().dom);
32670     
32671     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
32672     
32673     if(this.toolbar){
32674         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
32675     }
32676     // xtype created footer. - not sure if will work as we normally have to render first..
32677     if (this.footer && !this.footer.el && this.footer.xtype) {
32678         
32679         this.footer.container = this.grid.getView().getFooterPanel(true);
32680         this.footer.dataSource = this.grid.dataSource;
32681         this.footer = Roo.factory(this.footer, Roo);
32682         
32683     }
32684     
32685     grid.monitorWindowResize = false; // turn off autosizing
32686     grid.autoHeight = false;
32687     grid.autoWidth = false;
32688     this.grid = grid;
32689     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
32690 };
32691
32692 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
32693     getId : function(){
32694         return this.grid.id;
32695     },
32696     
32697     /**
32698      * Returns the grid for this panel
32699      * @return {Roo.grid.Grid} 
32700      */
32701     getGrid : function(){
32702         return this.grid;    
32703     },
32704     
32705     setSize : function(width, height){
32706         if(!this.ignoreResize(width, height)){
32707             var grid = this.grid;
32708             var size = this.adjustForComponents(width, height);
32709             grid.getGridEl().setSize(size.width, size.height);
32710             grid.autoSize();
32711         }
32712     },
32713     
32714     beforeSlide : function(){
32715         this.grid.getView().scroller.clip();
32716     },
32717     
32718     afterSlide : function(){
32719         this.grid.getView().scroller.unclip();
32720     },
32721     
32722     destroy : function(){
32723         this.grid.destroy();
32724         delete this.grid;
32725         Roo.GridPanel.superclass.destroy.call(this); 
32726     }
32727 });
32728
32729
32730 /**
32731  * @class Roo.NestedLayoutPanel
32732  * @extends Roo.ContentPanel
32733  * @constructor
32734  * Create a new NestedLayoutPanel.
32735  * 
32736  * 
32737  * @param {Roo.BorderLayout} layout The layout for this panel
32738  * @param {String/Object} config A string to set only the title or a config object
32739  */
32740 Roo.NestedLayoutPanel = function(layout, config)
32741 {
32742     // construct with only one argument..
32743     /* FIXME - implement nicer consturctors
32744     if (layout.layout) {
32745         config = layout;
32746         layout = config.layout;
32747         delete config.layout;
32748     }
32749     if (layout.xtype && !layout.getEl) {
32750         // then layout needs constructing..
32751         layout = Roo.factory(layout, Roo);
32752     }
32753     */
32754     
32755     
32756     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
32757     
32758     layout.monitorWindowResize = false; // turn off autosizing
32759     this.layout = layout;
32760     this.layout.getEl().addClass("x-layout-nested-layout");
32761     
32762     
32763     
32764     
32765 };
32766
32767 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
32768
32769     setSize : function(width, height){
32770         if(!this.ignoreResize(width, height)){
32771             var size = this.adjustForComponents(width, height);
32772             var el = this.layout.getEl();
32773             el.setSize(size.width, size.height);
32774             var touch = el.dom.offsetWidth;
32775             this.layout.layout();
32776             // ie requires a double layout on the first pass
32777             if(Roo.isIE && !this.initialized){
32778                 this.initialized = true;
32779                 this.layout.layout();
32780             }
32781         }
32782     },
32783     
32784     // activate all subpanels if not currently active..
32785     
32786     setActiveState : function(active){
32787         this.active = active;
32788         if(!active){
32789             this.fireEvent("deactivate", this);
32790             return;
32791         }
32792         
32793         this.fireEvent("activate", this);
32794         // not sure if this should happen before or after..
32795         if (!this.layout) {
32796             return; // should not happen..
32797         }
32798         var reg = false;
32799         for (var r in this.layout.regions) {
32800             reg = this.layout.getRegion(r);
32801             if (reg.getActivePanel()) {
32802                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
32803                 reg.setActivePanel(reg.getActivePanel());
32804                 continue;
32805             }
32806             if (!reg.panels.length) {
32807                 continue;
32808             }
32809             reg.showPanel(reg.getPanel(0));
32810         }
32811         
32812         
32813         
32814         
32815     },
32816     
32817     /**
32818      * Returns the nested BorderLayout for this panel
32819      * @return {Roo.BorderLayout} 
32820      */
32821     getLayout : function(){
32822         return this.layout;
32823     },
32824     
32825      /**
32826      * Adds a xtype elements to the layout of the nested panel
32827      * <pre><code>
32828
32829 panel.addxtype({
32830        xtype : 'ContentPanel',
32831        region: 'west',
32832        items: [ .... ]
32833    }
32834 );
32835
32836 panel.addxtype({
32837         xtype : 'NestedLayoutPanel',
32838         region: 'west',
32839         layout: {
32840            center: { },
32841            west: { }   
32842         },
32843         items : [ ... list of content panels or nested layout panels.. ]
32844    }
32845 );
32846 </code></pre>
32847      * @param {Object} cfg Xtype definition of item to add.
32848      */
32849     addxtype : function(cfg) {
32850         return this.layout.addxtype(cfg);
32851     
32852     }
32853 });
32854
32855 Roo.ScrollPanel = function(el, config, content){
32856     config = config || {};
32857     config.fitToFrame = true;
32858     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
32859     
32860     this.el.dom.style.overflow = "hidden";
32861     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
32862     this.el.removeClass("x-layout-inactive-content");
32863     this.el.on("mousewheel", this.onWheel, this);
32864
32865     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
32866     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
32867     up.unselectable(); down.unselectable();
32868     up.on("click", this.scrollUp, this);
32869     down.on("click", this.scrollDown, this);
32870     up.addClassOnOver("x-scroller-btn-over");
32871     down.addClassOnOver("x-scroller-btn-over");
32872     up.addClassOnClick("x-scroller-btn-click");
32873     down.addClassOnClick("x-scroller-btn-click");
32874     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
32875
32876     this.resizeEl = this.el;
32877     this.el = wrap; this.up = up; this.down = down;
32878 };
32879
32880 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
32881     increment : 100,
32882     wheelIncrement : 5,
32883     scrollUp : function(){
32884         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
32885     },
32886
32887     scrollDown : function(){
32888         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
32889     },
32890
32891     afterScroll : function(){
32892         var el = this.resizeEl;
32893         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
32894         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32895         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32896     },
32897
32898     setSize : function(){
32899         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
32900         this.afterScroll();
32901     },
32902
32903     onWheel : function(e){
32904         var d = e.getWheelDelta();
32905         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
32906         this.afterScroll();
32907         e.stopEvent();
32908     },
32909
32910     setContent : function(content, loadScripts){
32911         this.resizeEl.update(content, loadScripts);
32912     }
32913
32914 });
32915
32916
32917
32918
32919
32920
32921
32922
32923
32924 /**
32925  * @class Roo.TreePanel
32926  * @extends Roo.ContentPanel
32927  * @constructor
32928  * Create a new TreePanel. - defaults to fit/scoll contents.
32929  * @param {String/Object} config A string to set only the panel's title, or a config object
32930  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32931  */
32932 Roo.TreePanel = function(config){
32933     var el = config.el;
32934     var tree = config.tree;
32935     delete config.tree; 
32936     delete config.el; // hopefull!
32937     
32938     // wrapper for IE7 strict & safari scroll issue
32939     
32940     var treeEl = el.createChild();
32941     config.resizeEl = treeEl;
32942     
32943     
32944     
32945     Roo.TreePanel.superclass.constructor.call(this, el, config);
32946  
32947  
32948     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32949     //console.log(tree);
32950     this.on('activate', function()
32951     {
32952         if (this.tree.rendered) {
32953             return;
32954         }
32955         //console.log('render tree');
32956         this.tree.render();
32957     });
32958     
32959     this.on('resize',  function (cp, w, h) {
32960             this.tree.innerCt.setWidth(w);
32961             this.tree.innerCt.setHeight(h);
32962             this.tree.innerCt.setStyle('overflow-y', 'auto');
32963     });
32964
32965         
32966     
32967 };
32968
32969 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32970     fitToFrame : true,
32971     autoScroll : true
32972 });
32973
32974
32975
32976
32977
32978
32979
32980
32981
32982
32983
32984 /*
32985  * Based on:
32986  * Ext JS Library 1.1.1
32987  * Copyright(c) 2006-2007, Ext JS, LLC.
32988  *
32989  * Originally Released Under LGPL - original licence link has changed is not relivant.
32990  *
32991  * Fork - LGPL
32992  * <script type="text/javascript">
32993  */
32994  
32995
32996 /**
32997  * @class Roo.ReaderLayout
32998  * @extends Roo.BorderLayout
32999  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
33000  * center region containing two nested regions (a top one for a list view and one for item preview below),
33001  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
33002  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
33003  * expedites the setup of the overall layout and regions for this common application style.
33004  * Example:
33005  <pre><code>
33006 var reader = new Roo.ReaderLayout();
33007 var CP = Roo.ContentPanel;  // shortcut for adding
33008
33009 reader.beginUpdate();
33010 reader.add("north", new CP("north", "North"));
33011 reader.add("west", new CP("west", {title: "West"}));
33012 reader.add("east", new CP("east", {title: "East"}));
33013
33014 reader.regions.listView.add(new CP("listView", "List"));
33015 reader.regions.preview.add(new CP("preview", "Preview"));
33016 reader.endUpdate();
33017 </code></pre>
33018 * @constructor
33019 * Create a new ReaderLayout
33020 * @param {Object} config Configuration options
33021 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
33022 * document.body if omitted)
33023 */
33024 Roo.ReaderLayout = function(config, renderTo){
33025     var c = config || {size:{}};
33026     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
33027         north: c.north !== false ? Roo.apply({
33028             split:false,
33029             initialSize: 32,
33030             titlebar: false
33031         }, c.north) : false,
33032         west: c.west !== false ? Roo.apply({
33033             split:true,
33034             initialSize: 200,
33035             minSize: 175,
33036             maxSize: 400,
33037             titlebar: true,
33038             collapsible: true,
33039             animate: true,
33040             margins:{left:5,right:0,bottom:5,top:5},
33041             cmargins:{left:5,right:5,bottom:5,top:5}
33042         }, c.west) : false,
33043         east: c.east !== false ? Roo.apply({
33044             split:true,
33045             initialSize: 200,
33046             minSize: 175,
33047             maxSize: 400,
33048             titlebar: true,
33049             collapsible: true,
33050             animate: true,
33051             margins:{left:0,right:5,bottom:5,top:5},
33052             cmargins:{left:5,right:5,bottom:5,top:5}
33053         }, c.east) : false,
33054         center: Roo.apply({
33055             tabPosition: 'top',
33056             autoScroll:false,
33057             closeOnTab: true,
33058             titlebar:false,
33059             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
33060         }, c.center)
33061     });
33062
33063     this.el.addClass('x-reader');
33064
33065     this.beginUpdate();
33066
33067     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
33068         south: c.preview !== false ? Roo.apply({
33069             split:true,
33070             initialSize: 200,
33071             minSize: 100,
33072             autoScroll:true,
33073             collapsible:true,
33074             titlebar: true,
33075             cmargins:{top:5,left:0, right:0, bottom:0}
33076         }, c.preview) : false,
33077         center: Roo.apply({
33078             autoScroll:false,
33079             titlebar:false,
33080             minHeight:200
33081         }, c.listView)
33082     });
33083     this.add('center', new Roo.NestedLayoutPanel(inner,
33084             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
33085
33086     this.endUpdate();
33087
33088     this.regions.preview = inner.getRegion('south');
33089     this.regions.listView = inner.getRegion('center');
33090 };
33091
33092 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
33093  * Based on:
33094  * Ext JS Library 1.1.1
33095  * Copyright(c) 2006-2007, Ext JS, LLC.
33096  *
33097  * Originally Released Under LGPL - original licence link has changed is not relivant.
33098  *
33099  * Fork - LGPL
33100  * <script type="text/javascript">
33101  */
33102  
33103 /**
33104  * @class Roo.grid.Grid
33105  * @extends Roo.util.Observable
33106  * This class represents the primary interface of a component based grid control.
33107  * <br><br>Usage:<pre><code>
33108  var grid = new Roo.grid.Grid("my-container-id", {
33109      ds: myDataStore,
33110      cm: myColModel,
33111      selModel: mySelectionModel,
33112      autoSizeColumns: true,
33113      monitorWindowResize: false,
33114      trackMouseOver: true
33115  });
33116  // set any options
33117  grid.render();
33118  * </code></pre>
33119  * <b>Common Problems:</b><br/>
33120  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
33121  * element will correct this<br/>
33122  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
33123  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
33124  * are unpredictable.<br/>
33125  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
33126  * grid to calculate dimensions/offsets.<br/>
33127   * @constructor
33128  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
33129  * The container MUST have some type of size defined for the grid to fill. The container will be
33130  * automatically set to position relative if it isn't already.
33131  * @param {Object} config A config object that sets properties on this grid.
33132  */
33133 Roo.grid.Grid = function(container, config){
33134         // initialize the container
33135         this.container = Roo.get(container);
33136         this.container.update("");
33137         this.container.setStyle("overflow", "hidden");
33138     this.container.addClass('x-grid-container');
33139
33140     this.id = this.container.id;
33141
33142     Roo.apply(this, config);
33143     // check and correct shorthanded configs
33144     if(this.ds){
33145         this.dataSource = this.ds;
33146         delete this.ds;
33147     }
33148     if(this.cm){
33149         this.colModel = this.cm;
33150         delete this.cm;
33151     }
33152     if(this.sm){
33153         this.selModel = this.sm;
33154         delete this.sm;
33155     }
33156
33157     if (this.selModel) {
33158         this.selModel = Roo.factory(this.selModel, Roo.grid);
33159         this.sm = this.selModel;
33160         this.sm.xmodule = this.xmodule || false;
33161     }
33162     if (typeof(this.colModel.config) == 'undefined') {
33163         this.colModel = new Roo.grid.ColumnModel(this.colModel);
33164         this.cm = this.colModel;
33165         this.cm.xmodule = this.xmodule || false;
33166     }
33167     if (this.dataSource) {
33168         this.dataSource= Roo.factory(this.dataSource, Roo.data);
33169         this.ds = this.dataSource;
33170         this.ds.xmodule = this.xmodule || false;
33171          
33172     }
33173     
33174     
33175     
33176     if(this.width){
33177         this.container.setWidth(this.width);
33178     }
33179
33180     if(this.height){
33181         this.container.setHeight(this.height);
33182     }
33183     /** @private */
33184         this.addEvents({
33185         // raw events
33186         /**
33187          * @event click
33188          * The raw click event for the entire grid.
33189          * @param {Roo.EventObject} e
33190          */
33191         "click" : true,
33192         /**
33193          * @event dblclick
33194          * The raw dblclick event for the entire grid.
33195          * @param {Roo.EventObject} e
33196          */
33197         "dblclick" : true,
33198         /**
33199          * @event contextmenu
33200          * The raw contextmenu event for the entire grid.
33201          * @param {Roo.EventObject} e
33202          */
33203         "contextmenu" : true,
33204         /**
33205          * @event mousedown
33206          * The raw mousedown event for the entire grid.
33207          * @param {Roo.EventObject} e
33208          */
33209         "mousedown" : true,
33210         /**
33211          * @event mouseup
33212          * The raw mouseup event for the entire grid.
33213          * @param {Roo.EventObject} e
33214          */
33215         "mouseup" : true,
33216         /**
33217          * @event mouseover
33218          * The raw mouseover event for the entire grid.
33219          * @param {Roo.EventObject} e
33220          */
33221         "mouseover" : true,
33222         /**
33223          * @event mouseout
33224          * The raw mouseout event for the entire grid.
33225          * @param {Roo.EventObject} e
33226          */
33227         "mouseout" : true,
33228         /**
33229          * @event keypress
33230          * The raw keypress event for the entire grid.
33231          * @param {Roo.EventObject} e
33232          */
33233         "keypress" : true,
33234         /**
33235          * @event keydown
33236          * The raw keydown event for the entire grid.
33237          * @param {Roo.EventObject} e
33238          */
33239         "keydown" : true,
33240
33241         // custom events
33242
33243         /**
33244          * @event cellclick
33245          * Fires when a cell is clicked
33246          * @param {Grid} this
33247          * @param {Number} rowIndex
33248          * @param {Number} columnIndex
33249          * @param {Roo.EventObject} e
33250          */
33251         "cellclick" : true,
33252         /**
33253          * @event celldblclick
33254          * Fires when a cell is double clicked
33255          * @param {Grid} this
33256          * @param {Number} rowIndex
33257          * @param {Number} columnIndex
33258          * @param {Roo.EventObject} e
33259          */
33260         "celldblclick" : true,
33261         /**
33262          * @event rowclick
33263          * Fires when a row is clicked
33264          * @param {Grid} this
33265          * @param {Number} rowIndex
33266          * @param {Roo.EventObject} e
33267          */
33268         "rowclick" : true,
33269         /**
33270          * @event rowdblclick
33271          * Fires when a row is double clicked
33272          * @param {Grid} this
33273          * @param {Number} rowIndex
33274          * @param {Roo.EventObject} e
33275          */
33276         "rowdblclick" : true,
33277         /**
33278          * @event headerclick
33279          * Fires when a header is clicked
33280          * @param {Grid} this
33281          * @param {Number} columnIndex
33282          * @param {Roo.EventObject} e
33283          */
33284         "headerclick" : true,
33285         /**
33286          * @event headerdblclick
33287          * Fires when a header cell is double clicked
33288          * @param {Grid} this
33289          * @param {Number} columnIndex
33290          * @param {Roo.EventObject} e
33291          */
33292         "headerdblclick" : true,
33293         /**
33294          * @event rowcontextmenu
33295          * Fires when a row is right clicked
33296          * @param {Grid} this
33297          * @param {Number} rowIndex
33298          * @param {Roo.EventObject} e
33299          */
33300         "rowcontextmenu" : true,
33301         /**
33302          * @event cellcontextmenu
33303          * Fires when a cell is right clicked
33304          * @param {Grid} this
33305          * @param {Number} rowIndex
33306          * @param {Number} cellIndex
33307          * @param {Roo.EventObject} e
33308          */
33309          "cellcontextmenu" : true,
33310         /**
33311          * @event headercontextmenu
33312          * Fires when a header is right clicked
33313          * @param {Grid} this
33314          * @param {Number} columnIndex
33315          * @param {Roo.EventObject} e
33316          */
33317         "headercontextmenu" : true,
33318         /**
33319          * @event bodyscroll
33320          * Fires when the body element is scrolled
33321          * @param {Number} scrollLeft
33322          * @param {Number} scrollTop
33323          */
33324         "bodyscroll" : true,
33325         /**
33326          * @event columnresize
33327          * Fires when the user resizes a column
33328          * @param {Number} columnIndex
33329          * @param {Number} newSize
33330          */
33331         "columnresize" : true,
33332         /**
33333          * @event columnmove
33334          * Fires when the user moves a column
33335          * @param {Number} oldIndex
33336          * @param {Number} newIndex
33337          */
33338         "columnmove" : true,
33339         /**
33340          * @event startdrag
33341          * Fires when row(s) start being dragged
33342          * @param {Grid} this
33343          * @param {Roo.GridDD} dd The drag drop object
33344          * @param {event} e The raw browser event
33345          */
33346         "startdrag" : true,
33347         /**
33348          * @event enddrag
33349          * Fires when a drag operation is complete
33350          * @param {Grid} this
33351          * @param {Roo.GridDD} dd The drag drop object
33352          * @param {event} e The raw browser event
33353          */
33354         "enddrag" : true,
33355         /**
33356          * @event dragdrop
33357          * Fires when dragged row(s) are dropped on a valid DD target
33358          * @param {Grid} this
33359          * @param {Roo.GridDD} dd The drag drop object
33360          * @param {String} targetId The target drag drop object
33361          * @param {event} e The raw browser event
33362          */
33363         "dragdrop" : true,
33364         /**
33365          * @event dragover
33366          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
33367          * @param {Grid} this
33368          * @param {Roo.GridDD} dd The drag drop object
33369          * @param {String} targetId The target drag drop object
33370          * @param {event} e The raw browser event
33371          */
33372         "dragover" : true,
33373         /**
33374          * @event dragenter
33375          *  Fires when the dragged row(s) first cross another DD target while being dragged
33376          * @param {Grid} this
33377          * @param {Roo.GridDD} dd The drag drop object
33378          * @param {String} targetId The target drag drop object
33379          * @param {event} e The raw browser event
33380          */
33381         "dragenter" : true,
33382         /**
33383          * @event dragout
33384          * Fires when the dragged row(s) leave another DD target while being dragged
33385          * @param {Grid} this
33386          * @param {Roo.GridDD} dd The drag drop object
33387          * @param {String} targetId The target drag drop object
33388          * @param {event} e The raw browser event
33389          */
33390         "dragout" : true,
33391         /**
33392          * @event rowclass
33393          * Fires when a row is rendered, so you can change add a style to it.
33394          * @param {GridView} gridview   The grid view
33395          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
33396          */
33397         'rowclass' : true,
33398
33399         /**
33400          * @event render
33401          * Fires when the grid is rendered
33402          * @param {Grid} grid
33403          */
33404         'render' : true
33405     });
33406
33407     Roo.grid.Grid.superclass.constructor.call(this);
33408 };
33409 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
33410     
33411     /**
33412      * @cfg {String} ddGroup - drag drop group.
33413      */
33414
33415     /**
33416      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
33417      */
33418     minColumnWidth : 25,
33419
33420     /**
33421      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
33422      * <b>on initial render.</b> It is more efficient to explicitly size the columns
33423      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
33424      */
33425     autoSizeColumns : false,
33426
33427     /**
33428      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
33429      */
33430     autoSizeHeaders : true,
33431
33432     /**
33433      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
33434      */
33435     monitorWindowResize : true,
33436
33437     /**
33438      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
33439      * rows measured to get a columns size. Default is 0 (all rows).
33440      */
33441     maxRowsToMeasure : 0,
33442
33443     /**
33444      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
33445      */
33446     trackMouseOver : true,
33447
33448     /**
33449     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
33450     */
33451     
33452     /**
33453     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
33454     */
33455     enableDragDrop : false,
33456     
33457     /**
33458     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
33459     */
33460     enableColumnMove : true,
33461     
33462     /**
33463     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
33464     */
33465     enableColumnHide : true,
33466     
33467     /**
33468     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
33469     */
33470     enableRowHeightSync : false,
33471     
33472     /**
33473     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
33474     */
33475     stripeRows : true,
33476     
33477     /**
33478     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
33479     */
33480     autoHeight : false,
33481
33482     /**
33483      * @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.
33484      */
33485     autoExpandColumn : false,
33486
33487     /**
33488     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
33489     * Default is 50.
33490     */
33491     autoExpandMin : 50,
33492
33493     /**
33494     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
33495     */
33496     autoExpandMax : 1000,
33497
33498     /**
33499     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
33500     */
33501     view : null,
33502
33503     /**
33504     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
33505     */
33506     loadMask : false,
33507     /**
33508     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
33509     */
33510     dropTarget: false,
33511     
33512    
33513     
33514     // private
33515     rendered : false,
33516
33517     /**
33518     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
33519     * of a fixed width. Default is false.
33520     */
33521     /**
33522     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
33523     */
33524     /**
33525      * Called once after all setup has been completed and the grid is ready to be rendered.
33526      * @return {Roo.grid.Grid} this
33527      */
33528     render : function()
33529     {
33530         var c = this.container;
33531         // try to detect autoHeight/width mode
33532         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
33533             this.autoHeight = true;
33534         }
33535         var view = this.getView();
33536         view.init(this);
33537
33538         c.on("click", this.onClick, this);
33539         c.on("dblclick", this.onDblClick, this);
33540         c.on("contextmenu", this.onContextMenu, this);
33541         c.on("keydown", this.onKeyDown, this);
33542
33543         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
33544
33545         this.getSelectionModel().init(this);
33546
33547         view.render();
33548
33549         if(this.loadMask){
33550             this.loadMask = new Roo.LoadMask(this.container,
33551                     Roo.apply({store:this.dataSource}, this.loadMask));
33552         }
33553         
33554         
33555         if (this.toolbar && this.toolbar.xtype) {
33556             this.toolbar.container = this.getView().getHeaderPanel(true);
33557             this.toolbar = new Roo.Toolbar(this.toolbar);
33558         }
33559         if (this.footer && this.footer.xtype) {
33560             this.footer.dataSource = this.getDataSource();
33561             this.footer.container = this.getView().getFooterPanel(true);
33562             this.footer = Roo.factory(this.footer, Roo);
33563         }
33564         if (this.dropTarget && this.dropTarget.xtype) {
33565             delete this.dropTarget.xtype;
33566             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
33567         }
33568         
33569         
33570         this.rendered = true;
33571         this.fireEvent('render', this);
33572         return this;
33573     },
33574
33575         /**
33576          * Reconfigures the grid to use a different Store and Column Model.
33577          * The View will be bound to the new objects and refreshed.
33578          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
33579          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
33580          */
33581     reconfigure : function(dataSource, colModel){
33582         if(this.loadMask){
33583             this.loadMask.destroy();
33584             this.loadMask = new Roo.LoadMask(this.container,
33585                     Roo.apply({store:dataSource}, this.loadMask));
33586         }
33587         this.view.bind(dataSource, colModel);
33588         this.dataSource = dataSource;
33589         this.colModel = colModel;
33590         this.view.refresh(true);
33591     },
33592
33593     // private
33594     onKeyDown : function(e){
33595         this.fireEvent("keydown", e);
33596     },
33597
33598     /**
33599      * Destroy this grid.
33600      * @param {Boolean} removeEl True to remove the element
33601      */
33602     destroy : function(removeEl, keepListeners){
33603         if(this.loadMask){
33604             this.loadMask.destroy();
33605         }
33606         var c = this.container;
33607         c.removeAllListeners();
33608         this.view.destroy();
33609         this.colModel.purgeListeners();
33610         if(!keepListeners){
33611             this.purgeListeners();
33612         }
33613         c.update("");
33614         if(removeEl === true){
33615             c.remove();
33616         }
33617     },
33618
33619     // private
33620     processEvent : function(name, e){
33621         this.fireEvent(name, e);
33622         var t = e.getTarget();
33623         var v = this.view;
33624         var header = v.findHeaderIndex(t);
33625         if(header !== false){
33626             this.fireEvent("header" + name, this, header, e);
33627         }else{
33628             var row = v.findRowIndex(t);
33629             var cell = v.findCellIndex(t);
33630             if(row !== false){
33631                 this.fireEvent("row" + name, this, row, e);
33632                 if(cell !== false){
33633                     this.fireEvent("cell" + name, this, row, cell, e);
33634                 }
33635             }
33636         }
33637     },
33638
33639     // private
33640     onClick : function(e){
33641         this.processEvent("click", e);
33642     },
33643
33644     // private
33645     onContextMenu : function(e, t){
33646         this.processEvent("contextmenu", e);
33647     },
33648
33649     // private
33650     onDblClick : function(e){
33651         this.processEvent("dblclick", e);
33652     },
33653
33654     // private
33655     walkCells : function(row, col, step, fn, scope){
33656         var cm = this.colModel, clen = cm.getColumnCount();
33657         var ds = this.dataSource, rlen = ds.getCount(), first = true;
33658         if(step < 0){
33659             if(col < 0){
33660                 row--;
33661                 first = false;
33662             }
33663             while(row >= 0){
33664                 if(!first){
33665                     col = clen-1;
33666                 }
33667                 first = false;
33668                 while(col >= 0){
33669                     if(fn.call(scope || this, row, col, cm) === true){
33670                         return [row, col];
33671                     }
33672                     col--;
33673                 }
33674                 row--;
33675             }
33676         } else {
33677             if(col >= clen){
33678                 row++;
33679                 first = false;
33680             }
33681             while(row < rlen){
33682                 if(!first){
33683                     col = 0;
33684                 }
33685                 first = false;
33686                 while(col < clen){
33687                     if(fn.call(scope || this, row, col, cm) === true){
33688                         return [row, col];
33689                     }
33690                     col++;
33691                 }
33692                 row++;
33693             }
33694         }
33695         return null;
33696     },
33697
33698     // private
33699     getSelections : function(){
33700         return this.selModel.getSelections();
33701     },
33702
33703     /**
33704      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
33705      * but if manual update is required this method will initiate it.
33706      */
33707     autoSize : function(){
33708         if(this.rendered){
33709             this.view.layout();
33710             if(this.view.adjustForScroll){
33711                 this.view.adjustForScroll();
33712             }
33713         }
33714     },
33715
33716     /**
33717      * Returns the grid's underlying element.
33718      * @return {Element} The element
33719      */
33720     getGridEl : function(){
33721         return this.container;
33722     },
33723
33724     // private for compatibility, overridden by editor grid
33725     stopEditing : function(){},
33726
33727     /**
33728      * Returns the grid's SelectionModel.
33729      * @return {SelectionModel}
33730      */
33731     getSelectionModel : function(){
33732         if(!this.selModel){
33733             this.selModel = new Roo.grid.RowSelectionModel();
33734         }
33735         return this.selModel;
33736     },
33737
33738     /**
33739      * Returns the grid's DataSource.
33740      * @return {DataSource}
33741      */
33742     getDataSource : function(){
33743         return this.dataSource;
33744     },
33745
33746     /**
33747      * Returns the grid's ColumnModel.
33748      * @return {ColumnModel}
33749      */
33750     getColumnModel : function(){
33751         return this.colModel;
33752     },
33753
33754     /**
33755      * Returns the grid's GridView object.
33756      * @return {GridView}
33757      */
33758     getView : function(){
33759         if(!this.view){
33760             this.view = new Roo.grid.GridView(this.viewConfig);
33761         }
33762         return this.view;
33763     },
33764     /**
33765      * Called to get grid's drag proxy text, by default returns this.ddText.
33766      * @return {String}
33767      */
33768     getDragDropText : function(){
33769         var count = this.selModel.getCount();
33770         return String.format(this.ddText, count, count == 1 ? '' : 's');
33771     }
33772 });
33773 /**
33774  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
33775  * %0 is replaced with the number of selected rows.
33776  * @type String
33777  */
33778 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
33779  * Based on:
33780  * Ext JS Library 1.1.1
33781  * Copyright(c) 2006-2007, Ext JS, LLC.
33782  *
33783  * Originally Released Under LGPL - original licence link has changed is not relivant.
33784  *
33785  * Fork - LGPL
33786  * <script type="text/javascript">
33787  */
33788  
33789 Roo.grid.AbstractGridView = function(){
33790         this.grid = null;
33791         
33792         this.events = {
33793             "beforerowremoved" : true,
33794             "beforerowsinserted" : true,
33795             "beforerefresh" : true,
33796             "rowremoved" : true,
33797             "rowsinserted" : true,
33798             "rowupdated" : true,
33799             "refresh" : true
33800         };
33801     Roo.grid.AbstractGridView.superclass.constructor.call(this);
33802 };
33803
33804 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
33805     rowClass : "x-grid-row",
33806     cellClass : "x-grid-cell",
33807     tdClass : "x-grid-td",
33808     hdClass : "x-grid-hd",
33809     splitClass : "x-grid-hd-split",
33810     
33811         init: function(grid){
33812         this.grid = grid;
33813                 var cid = this.grid.getGridEl().id;
33814         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33815         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33816         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33817         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33818         },
33819         
33820         getColumnRenderers : function(){
33821         var renderers = [];
33822         var cm = this.grid.colModel;
33823         var colCount = cm.getColumnCount();
33824         for(var i = 0; i < colCount; i++){
33825             renderers[i] = cm.getRenderer(i);
33826         }
33827         return renderers;
33828     },
33829     
33830     getColumnIds : function(){
33831         var ids = [];
33832         var cm = this.grid.colModel;
33833         var colCount = cm.getColumnCount();
33834         for(var i = 0; i < colCount; i++){
33835             ids[i] = cm.getColumnId(i);
33836         }
33837         return ids;
33838     },
33839     
33840     getDataIndexes : function(){
33841         if(!this.indexMap){
33842             this.indexMap = this.buildIndexMap();
33843         }
33844         return this.indexMap.colToData;
33845     },
33846     
33847     getColumnIndexByDataIndex : function(dataIndex){
33848         if(!this.indexMap){
33849             this.indexMap = this.buildIndexMap();
33850         }
33851         return this.indexMap.dataToCol[dataIndex];
33852     },
33853     
33854     /**
33855      * Set a css style for a column dynamically. 
33856      * @param {Number} colIndex The index of the column
33857      * @param {String} name The css property name
33858      * @param {String} value The css value
33859      */
33860     setCSSStyle : function(colIndex, name, value){
33861         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33862         Roo.util.CSS.updateRule(selector, name, value);
33863     },
33864     
33865     generateRules : function(cm){
33866         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33867         Roo.util.CSS.removeStyleSheet(rulesId);
33868         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33869             var cid = cm.getColumnId(i);
33870             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33871                          this.tdSelector, cid, " {\n}\n",
33872                          this.hdSelector, cid, " {\n}\n",
33873                          this.splitSelector, cid, " {\n}\n");
33874         }
33875         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33876     }
33877 });/*
33878  * Based on:
33879  * Ext JS Library 1.1.1
33880  * Copyright(c) 2006-2007, Ext JS, LLC.
33881  *
33882  * Originally Released Under LGPL - original licence link has changed is not relivant.
33883  *
33884  * Fork - LGPL
33885  * <script type="text/javascript">
33886  */
33887
33888 // private
33889 // This is a support class used internally by the Grid components
33890 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33891     this.grid = grid;
33892     this.view = grid.getView();
33893     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33894     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33895     if(hd2){
33896         this.setHandleElId(Roo.id(hd));
33897         this.setOuterHandleElId(Roo.id(hd2));
33898     }
33899     this.scroll = false;
33900 };
33901 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33902     maxDragWidth: 120,
33903     getDragData : function(e){
33904         var t = Roo.lib.Event.getTarget(e);
33905         var h = this.view.findHeaderCell(t);
33906         if(h){
33907             return {ddel: h.firstChild, header:h};
33908         }
33909         return false;
33910     },
33911
33912     onInitDrag : function(e){
33913         this.view.headersDisabled = true;
33914         var clone = this.dragData.ddel.cloneNode(true);
33915         clone.id = Roo.id();
33916         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33917         this.proxy.update(clone);
33918         return true;
33919     },
33920
33921     afterValidDrop : function(){
33922         var v = this.view;
33923         setTimeout(function(){
33924             v.headersDisabled = false;
33925         }, 50);
33926     },
33927
33928     afterInvalidDrop : function(){
33929         var v = this.view;
33930         setTimeout(function(){
33931             v.headersDisabled = false;
33932         }, 50);
33933     }
33934 });
33935 /*
33936  * Based on:
33937  * Ext JS Library 1.1.1
33938  * Copyright(c) 2006-2007, Ext JS, LLC.
33939  *
33940  * Originally Released Under LGPL - original licence link has changed is not relivant.
33941  *
33942  * Fork - LGPL
33943  * <script type="text/javascript">
33944  */
33945 // private
33946 // This is a support class used internally by the Grid components
33947 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33948     this.grid = grid;
33949     this.view = grid.getView();
33950     // split the proxies so they don't interfere with mouse events
33951     this.proxyTop = Roo.DomHelper.append(document.body, {
33952         cls:"col-move-top", html:"&#160;"
33953     }, true);
33954     this.proxyBottom = Roo.DomHelper.append(document.body, {
33955         cls:"col-move-bottom", html:"&#160;"
33956     }, true);
33957     this.proxyTop.hide = this.proxyBottom.hide = function(){
33958         this.setLeftTop(-100,-100);
33959         this.setStyle("visibility", "hidden");
33960     };
33961     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33962     // temporarily disabled
33963     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33964     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33965 };
33966 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33967     proxyOffsets : [-4, -9],
33968     fly: Roo.Element.fly,
33969
33970     getTargetFromEvent : function(e){
33971         var t = Roo.lib.Event.getTarget(e);
33972         var cindex = this.view.findCellIndex(t);
33973         if(cindex !== false){
33974             return this.view.getHeaderCell(cindex);
33975         }
33976         return null;
33977     },
33978
33979     nextVisible : function(h){
33980         var v = this.view, cm = this.grid.colModel;
33981         h = h.nextSibling;
33982         while(h){
33983             if(!cm.isHidden(v.getCellIndex(h))){
33984                 return h;
33985             }
33986             h = h.nextSibling;
33987         }
33988         return null;
33989     },
33990
33991     prevVisible : function(h){
33992         var v = this.view, cm = this.grid.colModel;
33993         h = h.prevSibling;
33994         while(h){
33995             if(!cm.isHidden(v.getCellIndex(h))){
33996                 return h;
33997             }
33998             h = h.prevSibling;
33999         }
34000         return null;
34001     },
34002
34003     positionIndicator : function(h, n, e){
34004         var x = Roo.lib.Event.getPageX(e);
34005         var r = Roo.lib.Dom.getRegion(n.firstChild);
34006         var px, pt, py = r.top + this.proxyOffsets[1];
34007         if((r.right - x) <= (r.right-r.left)/2){
34008             px = r.right+this.view.borderWidth;
34009             pt = "after";
34010         }else{
34011             px = r.left;
34012             pt = "before";
34013         }
34014         var oldIndex = this.view.getCellIndex(h);
34015         var newIndex = this.view.getCellIndex(n);
34016
34017         if(this.grid.colModel.isFixed(newIndex)){
34018             return false;
34019         }
34020
34021         var locked = this.grid.colModel.isLocked(newIndex);
34022
34023         if(pt == "after"){
34024             newIndex++;
34025         }
34026         if(oldIndex < newIndex){
34027             newIndex--;
34028         }
34029         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
34030             return false;
34031         }
34032         px +=  this.proxyOffsets[0];
34033         this.proxyTop.setLeftTop(px, py);
34034         this.proxyTop.show();
34035         if(!this.bottomOffset){
34036             this.bottomOffset = this.view.mainHd.getHeight();
34037         }
34038         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
34039         this.proxyBottom.show();
34040         return pt;
34041     },
34042
34043     onNodeEnter : function(n, dd, e, data){
34044         if(data.header != n){
34045             this.positionIndicator(data.header, n, e);
34046         }
34047     },
34048
34049     onNodeOver : function(n, dd, e, data){
34050         var result = false;
34051         if(data.header != n){
34052             result = this.positionIndicator(data.header, n, e);
34053         }
34054         if(!result){
34055             this.proxyTop.hide();
34056             this.proxyBottom.hide();
34057         }
34058         return result ? this.dropAllowed : this.dropNotAllowed;
34059     },
34060
34061     onNodeOut : function(n, dd, e, data){
34062         this.proxyTop.hide();
34063         this.proxyBottom.hide();
34064     },
34065
34066     onNodeDrop : function(n, dd, e, data){
34067         var h = data.header;
34068         if(h != n){
34069             var cm = this.grid.colModel;
34070             var x = Roo.lib.Event.getPageX(e);
34071             var r = Roo.lib.Dom.getRegion(n.firstChild);
34072             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
34073             var oldIndex = this.view.getCellIndex(h);
34074             var newIndex = this.view.getCellIndex(n);
34075             var locked = cm.isLocked(newIndex);
34076             if(pt == "after"){
34077                 newIndex++;
34078             }
34079             if(oldIndex < newIndex){
34080                 newIndex--;
34081             }
34082             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
34083                 return false;
34084             }
34085             cm.setLocked(oldIndex, locked, true);
34086             cm.moveColumn(oldIndex, newIndex);
34087             this.grid.fireEvent("columnmove", oldIndex, newIndex);
34088             return true;
34089         }
34090         return false;
34091     }
34092 });
34093 /*
34094  * Based on:
34095  * Ext JS Library 1.1.1
34096  * Copyright(c) 2006-2007, Ext JS, LLC.
34097  *
34098  * Originally Released Under LGPL - original licence link has changed is not relivant.
34099  *
34100  * Fork - LGPL
34101  * <script type="text/javascript">
34102  */
34103   
34104 /**
34105  * @class Roo.grid.GridView
34106  * @extends Roo.util.Observable
34107  *
34108  * @constructor
34109  * @param {Object} config
34110  */
34111 Roo.grid.GridView = function(config){
34112     Roo.grid.GridView.superclass.constructor.call(this);
34113     this.el = null;
34114
34115     Roo.apply(this, config);
34116 };
34117
34118 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
34119
34120     
34121     rowClass : "x-grid-row",
34122
34123     cellClass : "x-grid-col",
34124
34125     tdClass : "x-grid-td",
34126
34127     hdClass : "x-grid-hd",
34128
34129     splitClass : "x-grid-split",
34130
34131     sortClasses : ["sort-asc", "sort-desc"],
34132
34133     enableMoveAnim : false,
34134
34135     hlColor: "C3DAF9",
34136
34137     dh : Roo.DomHelper,
34138
34139     fly : Roo.Element.fly,
34140
34141     css : Roo.util.CSS,
34142
34143     borderWidth: 1,
34144
34145     splitOffset: 3,
34146
34147     scrollIncrement : 22,
34148
34149     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
34150
34151     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
34152
34153     bind : function(ds, cm){
34154         if(this.ds){
34155             this.ds.un("load", this.onLoad, this);
34156             this.ds.un("datachanged", this.onDataChange, this);
34157             this.ds.un("add", this.onAdd, this);
34158             this.ds.un("remove", this.onRemove, this);
34159             this.ds.un("update", this.onUpdate, this);
34160             this.ds.un("clear", this.onClear, this);
34161         }
34162         if(ds){
34163             ds.on("load", this.onLoad, this);
34164             ds.on("datachanged", this.onDataChange, this);
34165             ds.on("add", this.onAdd, this);
34166             ds.on("remove", this.onRemove, this);
34167             ds.on("update", this.onUpdate, this);
34168             ds.on("clear", this.onClear, this);
34169         }
34170         this.ds = ds;
34171
34172         if(this.cm){
34173             this.cm.un("widthchange", this.onColWidthChange, this);
34174             this.cm.un("headerchange", this.onHeaderChange, this);
34175             this.cm.un("hiddenchange", this.onHiddenChange, this);
34176             this.cm.un("columnmoved", this.onColumnMove, this);
34177             this.cm.un("columnlockchange", this.onColumnLock, this);
34178         }
34179         if(cm){
34180             this.generateRules(cm);
34181             cm.on("widthchange", this.onColWidthChange, this);
34182             cm.on("headerchange", this.onHeaderChange, this);
34183             cm.on("hiddenchange", this.onHiddenChange, this);
34184             cm.on("columnmoved", this.onColumnMove, this);
34185             cm.on("columnlockchange", this.onColumnLock, this);
34186         }
34187         this.cm = cm;
34188     },
34189
34190     init: function(grid){
34191         Roo.grid.GridView.superclass.init.call(this, grid);
34192
34193         this.bind(grid.dataSource, grid.colModel);
34194
34195         grid.on("headerclick", this.handleHeaderClick, this);
34196
34197         if(grid.trackMouseOver){
34198             grid.on("mouseover", this.onRowOver, this);
34199             grid.on("mouseout", this.onRowOut, this);
34200         }
34201         grid.cancelTextSelection = function(){};
34202         this.gridId = grid.id;
34203
34204         var tpls = this.templates || {};
34205
34206         if(!tpls.master){
34207             tpls.master = new Roo.Template(
34208                '<div class="x-grid" hidefocus="true">',
34209                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
34210                   '<div class="x-grid-topbar"></div>',
34211                   '<div class="x-grid-scroller"><div></div></div>',
34212                   '<div class="x-grid-locked">',
34213                       '<div class="x-grid-header">{lockedHeader}</div>',
34214                       '<div class="x-grid-body">{lockedBody}</div>',
34215                   "</div>",
34216                   '<div class="x-grid-viewport">',
34217                       '<div class="x-grid-header">{header}</div>',
34218                       '<div class="x-grid-body">{body}</div>',
34219                   "</div>",
34220                   '<div class="x-grid-bottombar"></div>',
34221                  
34222                   '<div class="x-grid-resize-proxy">&#160;</div>',
34223                "</div>"
34224             );
34225             tpls.master.disableformats = true;
34226         }
34227
34228         if(!tpls.header){
34229             tpls.header = new Roo.Template(
34230                '<table border="0" cellspacing="0" cellpadding="0">',
34231                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
34232                "</table>{splits}"
34233             );
34234             tpls.header.disableformats = true;
34235         }
34236         tpls.header.compile();
34237
34238         if(!tpls.hcell){
34239             tpls.hcell = new Roo.Template(
34240                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
34241                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
34242                 "</div></td>"
34243              );
34244              tpls.hcell.disableFormats = true;
34245         }
34246         tpls.hcell.compile();
34247
34248         if(!tpls.hsplit){
34249             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
34250             tpls.hsplit.disableFormats = true;
34251         }
34252         tpls.hsplit.compile();
34253
34254         if(!tpls.body){
34255             tpls.body = new Roo.Template(
34256                '<table border="0" cellspacing="0" cellpadding="0">',
34257                "<tbody>{rows}</tbody>",
34258                "</table>"
34259             );
34260             tpls.body.disableFormats = true;
34261         }
34262         tpls.body.compile();
34263
34264         if(!tpls.row){
34265             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
34266             tpls.row.disableFormats = true;
34267         }
34268         tpls.row.compile();
34269
34270         if(!tpls.cell){
34271             tpls.cell = new Roo.Template(
34272                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
34273                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
34274                 "</td>"
34275             );
34276             tpls.cell.disableFormats = true;
34277         }
34278         tpls.cell.compile();
34279
34280         this.templates = tpls;
34281     },
34282
34283     // remap these for backwards compat
34284     onColWidthChange : function(){
34285         this.updateColumns.apply(this, arguments);
34286     },
34287     onHeaderChange : function(){
34288         this.updateHeaders.apply(this, arguments);
34289     }, 
34290     onHiddenChange : function(){
34291         this.handleHiddenChange.apply(this, arguments);
34292     },
34293     onColumnMove : function(){
34294         this.handleColumnMove.apply(this, arguments);
34295     },
34296     onColumnLock : function(){
34297         this.handleLockChange.apply(this, arguments);
34298     },
34299
34300     onDataChange : function(){
34301         this.refresh();
34302         this.updateHeaderSortState();
34303     },
34304
34305     onClear : function(){
34306         this.refresh();
34307     },
34308
34309     onUpdate : function(ds, record){
34310         this.refreshRow(record);
34311     },
34312
34313     refreshRow : function(record){
34314         var ds = this.ds, index;
34315         if(typeof record == 'number'){
34316             index = record;
34317             record = ds.getAt(index);
34318         }else{
34319             index = ds.indexOf(record);
34320         }
34321         this.insertRows(ds, index, index, true);
34322         this.onRemove(ds, record, index+1, true);
34323         this.syncRowHeights(index, index);
34324         this.layout();
34325         this.fireEvent("rowupdated", this, index, record);
34326     },
34327
34328     onAdd : function(ds, records, index){
34329         this.insertRows(ds, index, index + (records.length-1));
34330     },
34331
34332     onRemove : function(ds, record, index, isUpdate){
34333         if(isUpdate !== true){
34334             this.fireEvent("beforerowremoved", this, index, record);
34335         }
34336         var bt = this.getBodyTable(), lt = this.getLockedTable();
34337         if(bt.rows[index]){
34338             bt.firstChild.removeChild(bt.rows[index]);
34339         }
34340         if(lt.rows[index]){
34341             lt.firstChild.removeChild(lt.rows[index]);
34342         }
34343         if(isUpdate !== true){
34344             this.stripeRows(index);
34345             this.syncRowHeights(index, index);
34346             this.layout();
34347             this.fireEvent("rowremoved", this, index, record);
34348         }
34349     },
34350
34351     onLoad : function(){
34352         this.scrollToTop();
34353     },
34354
34355     /**
34356      * Scrolls the grid to the top
34357      */
34358     scrollToTop : function(){
34359         if(this.scroller){
34360             this.scroller.dom.scrollTop = 0;
34361             this.syncScroll();
34362         }
34363     },
34364
34365     /**
34366      * Gets a panel in the header of the grid that can be used for toolbars etc.
34367      * After modifying the contents of this panel a call to grid.autoSize() may be
34368      * required to register any changes in size.
34369      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
34370      * @return Roo.Element
34371      */
34372     getHeaderPanel : function(doShow){
34373         if(doShow){
34374             this.headerPanel.show();
34375         }
34376         return this.headerPanel;
34377     },
34378
34379     /**
34380      * Gets a panel in the footer of the grid that can be used for toolbars etc.
34381      * After modifying the contents of this panel a call to grid.autoSize() may be
34382      * required to register any changes in size.
34383      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
34384      * @return Roo.Element
34385      */
34386     getFooterPanel : function(doShow){
34387         if(doShow){
34388             this.footerPanel.show();
34389         }
34390         return this.footerPanel;
34391     },
34392
34393     initElements : function(){
34394         var E = Roo.Element;
34395         var el = this.grid.getGridEl().dom.firstChild;
34396         var cs = el.childNodes;
34397
34398         this.el = new E(el);
34399         
34400          this.focusEl = new E(el.firstChild);
34401         this.focusEl.swallowEvent("click", true);
34402         
34403         this.headerPanel = new E(cs[1]);
34404         this.headerPanel.enableDisplayMode("block");
34405
34406         this.scroller = new E(cs[2]);
34407         this.scrollSizer = new E(this.scroller.dom.firstChild);
34408
34409         this.lockedWrap = new E(cs[3]);
34410         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
34411         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
34412
34413         this.mainWrap = new E(cs[4]);
34414         this.mainHd = new E(this.mainWrap.dom.firstChild);
34415         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
34416
34417         this.footerPanel = new E(cs[5]);
34418         this.footerPanel.enableDisplayMode("block");
34419
34420         this.resizeProxy = new E(cs[6]);
34421
34422         this.headerSelector = String.format(
34423            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
34424            this.lockedHd.id, this.mainHd.id
34425         );
34426
34427         this.splitterSelector = String.format(
34428            '#{0} div.x-grid-split, #{1} div.x-grid-split',
34429            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
34430         );
34431     },
34432     idToCssName : function(s)
34433     {
34434         return s.replace(/[^a-z0-9]+/ig, '-');
34435     },
34436
34437     getHeaderCell : function(index){
34438         return Roo.DomQuery.select(this.headerSelector)[index];
34439     },
34440
34441     getHeaderCellMeasure : function(index){
34442         return this.getHeaderCell(index).firstChild;
34443     },
34444
34445     getHeaderCellText : function(index){
34446         return this.getHeaderCell(index).firstChild.firstChild;
34447     },
34448
34449     getLockedTable : function(){
34450         return this.lockedBody.dom.firstChild;
34451     },
34452
34453     getBodyTable : function(){
34454         return this.mainBody.dom.firstChild;
34455     },
34456
34457     getLockedRow : function(index){
34458         return this.getLockedTable().rows[index];
34459     },
34460
34461     getRow : function(index){
34462         return this.getBodyTable().rows[index];
34463     },
34464
34465     getRowComposite : function(index){
34466         if(!this.rowEl){
34467             this.rowEl = new Roo.CompositeElementLite();
34468         }
34469         var els = [], lrow, mrow;
34470         if(lrow = this.getLockedRow(index)){
34471             els.push(lrow);
34472         }
34473         if(mrow = this.getRow(index)){
34474             els.push(mrow);
34475         }
34476         this.rowEl.elements = els;
34477         return this.rowEl;
34478     },
34479     /**
34480      * Gets the 'td' of the cell
34481      * 
34482      * @param {Integer} rowIndex row to select
34483      * @param {Integer} colIndex column to select
34484      * 
34485      * @return {Object} 
34486      */
34487     getCell : function(rowIndex, colIndex){
34488         var locked = this.cm.getLockedCount();
34489         var source;
34490         if(colIndex < locked){
34491             source = this.lockedBody.dom.firstChild;
34492         }else{
34493             source = this.mainBody.dom.firstChild;
34494             colIndex -= locked;
34495         }
34496         return source.rows[rowIndex].childNodes[colIndex];
34497     },
34498
34499     getCellText : function(rowIndex, colIndex){
34500         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
34501     },
34502
34503     getCellBox : function(cell){
34504         var b = this.fly(cell).getBox();
34505         if(Roo.isOpera){ // opera fails to report the Y
34506             b.y = cell.offsetTop + this.mainBody.getY();
34507         }
34508         return b;
34509     },
34510
34511     getCellIndex : function(cell){
34512         var id = String(cell.className).match(this.cellRE);
34513         if(id){
34514             return parseInt(id[1], 10);
34515         }
34516         return 0;
34517     },
34518
34519     findHeaderIndex : function(n){
34520         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34521         return r ? this.getCellIndex(r) : false;
34522     },
34523
34524     findHeaderCell : function(n){
34525         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34526         return r ? r : false;
34527     },
34528
34529     findRowIndex : function(n){
34530         if(!n){
34531             return false;
34532         }
34533         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
34534         return r ? r.rowIndex : false;
34535     },
34536
34537     findCellIndex : function(node){
34538         var stop = this.el.dom;
34539         while(node && node != stop){
34540             if(this.findRE.test(node.className)){
34541                 return this.getCellIndex(node);
34542             }
34543             node = node.parentNode;
34544         }
34545         return false;
34546     },
34547
34548     getColumnId : function(index){
34549         return this.cm.getColumnId(index);
34550     },
34551
34552     getSplitters : function()
34553     {
34554         if(this.splitterSelector){
34555            return Roo.DomQuery.select(this.splitterSelector);
34556         }else{
34557             return null;
34558       }
34559     },
34560
34561     getSplitter : function(index){
34562         return this.getSplitters()[index];
34563     },
34564
34565     onRowOver : function(e, t){
34566         var row;
34567         if((row = this.findRowIndex(t)) !== false){
34568             this.getRowComposite(row).addClass("x-grid-row-over");
34569         }
34570     },
34571
34572     onRowOut : function(e, t){
34573         var row;
34574         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
34575             this.getRowComposite(row).removeClass("x-grid-row-over");
34576         }
34577     },
34578
34579     renderHeaders : function(){
34580         var cm = this.cm;
34581         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
34582         var cb = [], lb = [], sb = [], lsb = [], p = {};
34583         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34584             p.cellId = "x-grid-hd-0-" + i;
34585             p.splitId = "x-grid-csplit-0-" + i;
34586             p.id = cm.getColumnId(i);
34587             p.title = cm.getColumnTooltip(i) || "";
34588             p.value = cm.getColumnHeader(i) || "";
34589             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
34590             if(!cm.isLocked(i)){
34591                 cb[cb.length] = ct.apply(p);
34592                 sb[sb.length] = st.apply(p);
34593             }else{
34594                 lb[lb.length] = ct.apply(p);
34595                 lsb[lsb.length] = st.apply(p);
34596             }
34597         }
34598         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
34599                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
34600     },
34601
34602     updateHeaders : function(){
34603         var html = this.renderHeaders();
34604         this.lockedHd.update(html[0]);
34605         this.mainHd.update(html[1]);
34606     },
34607
34608     /**
34609      * Focuses the specified row.
34610      * @param {Number} row The row index
34611      */
34612     focusRow : function(row)
34613     {
34614         //Roo.log('GridView.focusRow');
34615         var x = this.scroller.dom.scrollLeft;
34616         this.focusCell(row, 0, false);
34617         this.scroller.dom.scrollLeft = x;
34618     },
34619
34620     /**
34621      * Focuses the specified cell.
34622      * @param {Number} row The row index
34623      * @param {Number} col The column index
34624      * @param {Boolean} hscroll false to disable horizontal scrolling
34625      */
34626     focusCell : function(row, col, hscroll)
34627     {
34628         //Roo.log('GridView.focusCell');
34629         var el = this.ensureVisible(row, col, hscroll);
34630         this.focusEl.alignTo(el, "tl-tl");
34631         if(Roo.isGecko){
34632             this.focusEl.focus();
34633         }else{
34634             this.focusEl.focus.defer(1, this.focusEl);
34635         }
34636     },
34637
34638     /**
34639      * Scrolls the specified cell into view
34640      * @param {Number} row The row index
34641      * @param {Number} col The column index
34642      * @param {Boolean} hscroll false to disable horizontal scrolling
34643      */
34644     ensureVisible : function(row, col, hscroll)
34645     {
34646         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
34647         //return null; //disable for testing.
34648         if(typeof row != "number"){
34649             row = row.rowIndex;
34650         }
34651         if(row < 0 && row >= this.ds.getCount()){
34652             return  null;
34653         }
34654         col = (col !== undefined ? col : 0);
34655         var cm = this.grid.colModel;
34656         while(cm.isHidden(col)){
34657             col++;
34658         }
34659
34660         var el = this.getCell(row, col);
34661         if(!el){
34662             return null;
34663         }
34664         var c = this.scroller.dom;
34665
34666         var ctop = parseInt(el.offsetTop, 10);
34667         var cleft = parseInt(el.offsetLeft, 10);
34668         var cbot = ctop + el.offsetHeight;
34669         var cright = cleft + el.offsetWidth;
34670         
34671         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
34672         var stop = parseInt(c.scrollTop, 10);
34673         var sleft = parseInt(c.scrollLeft, 10);
34674         var sbot = stop + ch;
34675         var sright = sleft + c.clientWidth;
34676         /*
34677         Roo.log('GridView.ensureVisible:' +
34678                 ' ctop:' + ctop +
34679                 ' c.clientHeight:' + c.clientHeight +
34680                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
34681                 ' stop:' + stop +
34682                 ' cbot:' + cbot +
34683                 ' sbot:' + sbot +
34684                 ' ch:' + ch  
34685                 );
34686         */
34687         if(ctop < stop){
34688              c.scrollTop = ctop;
34689             //Roo.log("set scrolltop to ctop DISABLE?");
34690         }else if(cbot > sbot){
34691             //Roo.log("set scrolltop to cbot-ch");
34692             c.scrollTop = cbot-ch;
34693         }
34694         
34695         if(hscroll !== false){
34696             if(cleft < sleft){
34697                 c.scrollLeft = cleft;
34698             }else if(cright > sright){
34699                 c.scrollLeft = cright-c.clientWidth;
34700             }
34701         }
34702          
34703         return el;
34704     },
34705
34706     updateColumns : function(){
34707         this.grid.stopEditing();
34708         var cm = this.grid.colModel, colIds = this.getColumnIds();
34709         //var totalWidth = cm.getTotalWidth();
34710         var pos = 0;
34711         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34712             //if(cm.isHidden(i)) continue;
34713             var w = cm.getColumnWidth(i);
34714             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34715             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34716         }
34717         this.updateSplitters();
34718     },
34719
34720     generateRules : function(cm){
34721         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
34722         Roo.util.CSS.removeStyleSheet(rulesId);
34723         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34724             var cid = cm.getColumnId(i);
34725             var align = '';
34726             if(cm.config[i].align){
34727                 align = 'text-align:'+cm.config[i].align+';';
34728             }
34729             var hidden = '';
34730             if(cm.isHidden(i)){
34731                 hidden = 'display:none;';
34732             }
34733             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
34734             ruleBuf.push(
34735                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
34736                     this.hdSelector, cid, " {\n", align, width, "}\n",
34737                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
34738                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
34739         }
34740         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34741     },
34742
34743     updateSplitters : function(){
34744         var cm = this.cm, s = this.getSplitters();
34745         if(s){ // splitters not created yet
34746             var pos = 0, locked = true;
34747             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34748                 if(cm.isHidden(i)) continue;
34749                 var w = cm.getColumnWidth(i); // make sure it's a number
34750                 if(!cm.isLocked(i) && locked){
34751                     pos = 0;
34752                     locked = false;
34753                 }
34754                 pos += w;
34755                 s[i].style.left = (pos-this.splitOffset) + "px";
34756             }
34757         }
34758     },
34759
34760     handleHiddenChange : function(colModel, colIndex, hidden){
34761         if(hidden){
34762             this.hideColumn(colIndex);
34763         }else{
34764             this.unhideColumn(colIndex);
34765         }
34766     },
34767
34768     hideColumn : function(colIndex){
34769         var cid = this.getColumnId(colIndex);
34770         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
34771         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
34772         if(Roo.isSafari){
34773             this.updateHeaders();
34774         }
34775         this.updateSplitters();
34776         this.layout();
34777     },
34778
34779     unhideColumn : function(colIndex){
34780         var cid = this.getColumnId(colIndex);
34781         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
34782         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
34783
34784         if(Roo.isSafari){
34785             this.updateHeaders();
34786         }
34787         this.updateSplitters();
34788         this.layout();
34789     },
34790
34791     insertRows : function(dm, firstRow, lastRow, isUpdate){
34792         if(firstRow == 0 && lastRow == dm.getCount()-1){
34793             this.refresh();
34794         }else{
34795             if(!isUpdate){
34796                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
34797             }
34798             var s = this.getScrollState();
34799             var markup = this.renderRows(firstRow, lastRow);
34800             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
34801             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
34802             this.restoreScroll(s);
34803             if(!isUpdate){
34804                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
34805                 this.syncRowHeights(firstRow, lastRow);
34806                 this.stripeRows(firstRow);
34807                 this.layout();
34808             }
34809         }
34810     },
34811
34812     bufferRows : function(markup, target, index){
34813         var before = null, trows = target.rows, tbody = target.tBodies[0];
34814         if(index < trows.length){
34815             before = trows[index];
34816         }
34817         var b = document.createElement("div");
34818         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34819         var rows = b.firstChild.rows;
34820         for(var i = 0, len = rows.length; i < len; i++){
34821             if(before){
34822                 tbody.insertBefore(rows[0], before);
34823             }else{
34824                 tbody.appendChild(rows[0]);
34825             }
34826         }
34827         b.innerHTML = "";
34828         b = null;
34829     },
34830
34831     deleteRows : function(dm, firstRow, lastRow){
34832         if(dm.getRowCount()<1){
34833             this.fireEvent("beforerefresh", this);
34834             this.mainBody.update("");
34835             this.lockedBody.update("");
34836             this.fireEvent("refresh", this);
34837         }else{
34838             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34839             var bt = this.getBodyTable();
34840             var tbody = bt.firstChild;
34841             var rows = bt.rows;
34842             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34843                 tbody.removeChild(rows[firstRow]);
34844             }
34845             this.stripeRows(firstRow);
34846             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34847         }
34848     },
34849
34850     updateRows : function(dataSource, firstRow, lastRow){
34851         var s = this.getScrollState();
34852         this.refresh();
34853         this.restoreScroll(s);
34854     },
34855
34856     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34857         if(!noRefresh){
34858            this.refresh();
34859         }
34860         this.updateHeaderSortState();
34861     },
34862
34863     getScrollState : function(){
34864         
34865         var sb = this.scroller.dom;
34866         return {left: sb.scrollLeft, top: sb.scrollTop};
34867     },
34868
34869     stripeRows : function(startRow){
34870         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34871             return;
34872         }
34873         startRow = startRow || 0;
34874         var rows = this.getBodyTable().rows;
34875         var lrows = this.getLockedTable().rows;
34876         var cls = ' x-grid-row-alt ';
34877         for(var i = startRow, len = rows.length; i < len; i++){
34878             var row = rows[i], lrow = lrows[i];
34879             var isAlt = ((i+1) % 2 == 0);
34880             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34881             if(isAlt == hasAlt){
34882                 continue;
34883             }
34884             if(isAlt){
34885                 row.className += " x-grid-row-alt";
34886             }else{
34887                 row.className = row.className.replace("x-grid-row-alt", "");
34888             }
34889             if(lrow){
34890                 lrow.className = row.className;
34891             }
34892         }
34893     },
34894
34895     restoreScroll : function(state){
34896         //Roo.log('GridView.restoreScroll');
34897         var sb = this.scroller.dom;
34898         sb.scrollLeft = state.left;
34899         sb.scrollTop = state.top;
34900         this.syncScroll();
34901     },
34902
34903     syncScroll : function(){
34904         //Roo.log('GridView.syncScroll');
34905         var sb = this.scroller.dom;
34906         var sh = this.mainHd.dom;
34907         var bs = this.mainBody.dom;
34908         var lv = this.lockedBody.dom;
34909         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34910         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34911     },
34912
34913     handleScroll : function(e){
34914         this.syncScroll();
34915         var sb = this.scroller.dom;
34916         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34917         e.stopEvent();
34918     },
34919
34920     handleWheel : function(e){
34921         var d = e.getWheelDelta();
34922         this.scroller.dom.scrollTop -= d*22;
34923         // set this here to prevent jumpy scrolling on large tables
34924         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34925         e.stopEvent();
34926     },
34927
34928     renderRows : function(startRow, endRow){
34929         // pull in all the crap needed to render rows
34930         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34931         var colCount = cm.getColumnCount();
34932
34933         if(ds.getCount() < 1){
34934             return ["", ""];
34935         }
34936
34937         // build a map for all the columns
34938         var cs = [];
34939         for(var i = 0; i < colCount; i++){
34940             var name = cm.getDataIndex(i);
34941             cs[i] = {
34942                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34943                 renderer : cm.getRenderer(i),
34944                 id : cm.getColumnId(i),
34945                 locked : cm.isLocked(i)
34946             };
34947         }
34948
34949         startRow = startRow || 0;
34950         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34951
34952         // records to render
34953         var rs = ds.getRange(startRow, endRow);
34954
34955         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34956     },
34957
34958     // As much as I hate to duplicate code, this was branched because FireFox really hates
34959     // [].join("") on strings. The performance difference was substantial enough to
34960     // branch this function
34961     doRender : Roo.isGecko ?
34962             function(cs, rs, ds, startRow, colCount, stripe){
34963                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34964                 // buffers
34965                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34966                 
34967                 var hasListener = this.grid.hasListener('rowclass');
34968                 var rowcfg = {};
34969                 for(var j = 0, len = rs.length; j < len; j++){
34970                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34971                     for(var i = 0; i < colCount; i++){
34972                         c = cs[i];
34973                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34974                         p.id = c.id;
34975                         p.css = p.attr = "";
34976                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34977                         if(p.value == undefined || p.value === "") p.value = "&#160;";
34978                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34979                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
34980                         }
34981                         var markup = ct.apply(p);
34982                         if(!c.locked){
34983                             cb+= markup;
34984                         }else{
34985                             lcb+= markup;
34986                         }
34987                     }
34988                     var alt = [];
34989                     if(stripe && ((rowIndex+1) % 2 == 0)){
34990                         alt.push("x-grid-row-alt")
34991                     }
34992                     if(r.dirty){
34993                         alt.push(  " x-grid-dirty-row");
34994                     }
34995                     rp.cells = lcb;
34996                     if(this.getRowClass){
34997                         alt.push(this.getRowClass(r, rowIndex));
34998                     }
34999                     if (hasListener) {
35000                         rowcfg = {
35001                              
35002                             record: r,
35003                             rowIndex : rowIndex,
35004                             rowClass : ''
35005                         }
35006                         this.grid.fireEvent('rowclass', this, rowcfg);
35007                         alt.push(rowcfg.rowClass);
35008                     }
35009                     rp.alt = alt.join(" ");
35010                     lbuf+= rt.apply(rp);
35011                     rp.cells = cb;
35012                     buf+=  rt.apply(rp);
35013                 }
35014                 return [lbuf, buf];
35015             } :
35016             function(cs, rs, ds, startRow, colCount, stripe){
35017                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35018                 // buffers
35019                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35020                 var hasListener = this.grid.hasListener('rowclass');
35021  
35022                 var rowcfg = {};
35023                 for(var j = 0, len = rs.length; j < len; j++){
35024                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
35025                     for(var i = 0; i < colCount; i++){
35026                         c = cs[i];
35027                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35028                         p.id = c.id;
35029                         p.css = p.attr = "";
35030                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35031                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35032                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35033                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35034                         }
35035                         
35036                         var markup = ct.apply(p);
35037                         if(!c.locked){
35038                             cb[cb.length] = markup;
35039                         }else{
35040                             lcb[lcb.length] = markup;
35041                         }
35042                     }
35043                     var alt = [];
35044                     if(stripe && ((rowIndex+1) % 2 == 0)){
35045                         alt.push( "x-grid-row-alt");
35046                     }
35047                     if(r.dirty){
35048                         alt.push(" x-grid-dirty-row");
35049                     }
35050                     rp.cells = lcb;
35051                     if(this.getRowClass){
35052                         alt.push( this.getRowClass(r, rowIndex));
35053                     }
35054                     if (hasListener) {
35055                         rowcfg = {
35056                              
35057                             record: r,
35058                             rowIndex : rowIndex,
35059                             rowClass : ''
35060                         }
35061                         this.grid.fireEvent('rowclass', this, rowcfg);
35062                         alt.push(rowcfg.rowClass);
35063                     }
35064                     rp.alt = alt.join(" ");
35065                     rp.cells = lcb.join("");
35066                     lbuf[lbuf.length] = rt.apply(rp);
35067                     rp.cells = cb.join("");
35068                     buf[buf.length] =  rt.apply(rp);
35069                 }
35070                 return [lbuf.join(""), buf.join("")];
35071             },
35072
35073     renderBody : function(){
35074         var markup = this.renderRows();
35075         var bt = this.templates.body;
35076         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
35077     },
35078
35079     /**
35080      * Refreshes the grid
35081      * @param {Boolean} headersToo
35082      */
35083     refresh : function(headersToo){
35084         this.fireEvent("beforerefresh", this);
35085         this.grid.stopEditing();
35086         var result = this.renderBody();
35087         this.lockedBody.update(result[0]);
35088         this.mainBody.update(result[1]);
35089         if(headersToo === true){
35090             this.updateHeaders();
35091             this.updateColumns();
35092             this.updateSplitters();
35093             this.updateHeaderSortState();
35094         }
35095         this.syncRowHeights();
35096         this.layout();
35097         this.fireEvent("refresh", this);
35098     },
35099
35100     handleColumnMove : function(cm, oldIndex, newIndex){
35101         this.indexMap = null;
35102         var s = this.getScrollState();
35103         this.refresh(true);
35104         this.restoreScroll(s);
35105         this.afterMove(newIndex);
35106     },
35107
35108     afterMove : function(colIndex){
35109         if(this.enableMoveAnim && Roo.enableFx){
35110             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
35111         }
35112         // if multisort - fix sortOrder, and reload..
35113         if (this.grid.dataSource.multiSort) {
35114             // the we can call sort again..
35115             var dm = this.grid.dataSource;
35116             var cm = this.grid.colModel;
35117             var so = [];
35118             for(var i = 0; i < cm.config.length; i++ ) {
35119                 
35120                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
35121                     continue; // dont' bother, it's not in sort list or being set.
35122                 }
35123                 
35124                 so.push(cm.config[i].dataIndex);
35125             };
35126             dm.sortOrder = so;
35127             dm.load(dm.lastOptions);
35128             
35129             
35130         }
35131         
35132     },
35133
35134     updateCell : function(dm, rowIndex, dataIndex){
35135         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
35136         if(typeof colIndex == "undefined"){ // not present in grid
35137             return;
35138         }
35139         var cm = this.grid.colModel;
35140         var cell = this.getCell(rowIndex, colIndex);
35141         var cellText = this.getCellText(rowIndex, colIndex);
35142
35143         var p = {
35144             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
35145             id : cm.getColumnId(colIndex),
35146             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
35147         };
35148         var renderer = cm.getRenderer(colIndex);
35149         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
35150         if(typeof val == "undefined" || val === "") val = "&#160;";
35151         cellText.innerHTML = val;
35152         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
35153         this.syncRowHeights(rowIndex, rowIndex);
35154     },
35155
35156     calcColumnWidth : function(colIndex, maxRowsToMeasure){
35157         var maxWidth = 0;
35158         if(this.grid.autoSizeHeaders){
35159             var h = this.getHeaderCellMeasure(colIndex);
35160             maxWidth = Math.max(maxWidth, h.scrollWidth);
35161         }
35162         var tb, index;
35163         if(this.cm.isLocked(colIndex)){
35164             tb = this.getLockedTable();
35165             index = colIndex;
35166         }else{
35167             tb = this.getBodyTable();
35168             index = colIndex - this.cm.getLockedCount();
35169         }
35170         if(tb && tb.rows){
35171             var rows = tb.rows;
35172             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
35173             for(var i = 0; i < stopIndex; i++){
35174                 var cell = rows[i].childNodes[index].firstChild;
35175                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
35176             }
35177         }
35178         return maxWidth + /*margin for error in IE*/ 5;
35179     },
35180     /**
35181      * Autofit a column to its content.
35182      * @param {Number} colIndex
35183      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
35184      */
35185      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
35186          if(this.cm.isHidden(colIndex)){
35187              return; // can't calc a hidden column
35188          }
35189         if(forceMinSize){
35190             var cid = this.cm.getColumnId(colIndex);
35191             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
35192            if(this.grid.autoSizeHeaders){
35193                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
35194            }
35195         }
35196         var newWidth = this.calcColumnWidth(colIndex);
35197         this.cm.setColumnWidth(colIndex,
35198             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
35199         if(!suppressEvent){
35200             this.grid.fireEvent("columnresize", colIndex, newWidth);
35201         }
35202     },
35203
35204     /**
35205      * Autofits all columns to their content and then expands to fit any extra space in the grid
35206      */
35207      autoSizeColumns : function(){
35208         var cm = this.grid.colModel;
35209         var colCount = cm.getColumnCount();
35210         for(var i = 0; i < colCount; i++){
35211             this.autoSizeColumn(i, true, true);
35212         }
35213         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
35214             this.fitColumns();
35215         }else{
35216             this.updateColumns();
35217             this.layout();
35218         }
35219     },
35220
35221     /**
35222      * Autofits all columns to the grid's width proportionate with their current size
35223      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
35224      */
35225     fitColumns : function(reserveScrollSpace){
35226         var cm = this.grid.colModel;
35227         var colCount = cm.getColumnCount();
35228         var cols = [];
35229         var width = 0;
35230         var i, w;
35231         for (i = 0; i < colCount; i++){
35232             if(!cm.isHidden(i) && !cm.isFixed(i)){
35233                 w = cm.getColumnWidth(i);
35234                 cols.push(i);
35235                 cols.push(w);
35236                 width += w;
35237             }
35238         }
35239         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
35240         if(reserveScrollSpace){
35241             avail -= 17;
35242         }
35243         var frac = (avail - cm.getTotalWidth())/width;
35244         while (cols.length){
35245             w = cols.pop();
35246             i = cols.pop();
35247             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
35248         }
35249         this.updateColumns();
35250         this.layout();
35251     },
35252
35253     onRowSelect : function(rowIndex){
35254         var row = this.getRowComposite(rowIndex);
35255         row.addClass("x-grid-row-selected");
35256     },
35257
35258     onRowDeselect : function(rowIndex){
35259         var row = this.getRowComposite(rowIndex);
35260         row.removeClass("x-grid-row-selected");
35261     },
35262
35263     onCellSelect : function(row, col){
35264         var cell = this.getCell(row, col);
35265         if(cell){
35266             Roo.fly(cell).addClass("x-grid-cell-selected");
35267         }
35268     },
35269
35270     onCellDeselect : function(row, col){
35271         var cell = this.getCell(row, col);
35272         if(cell){
35273             Roo.fly(cell).removeClass("x-grid-cell-selected");
35274         }
35275     },
35276
35277     updateHeaderSortState : function(){
35278         
35279         // sort state can be single { field: xxx, direction : yyy}
35280         // or   { xxx=>ASC , yyy : DESC ..... }
35281         
35282         var mstate = {};
35283         if (!this.ds.multiSort) { 
35284             var state = this.ds.getSortState();
35285             if(!state){
35286                 return;
35287             }
35288             mstate[state.field] = state.direction;
35289             // FIXME... - this is not used here.. but might be elsewhere..
35290             this.sortState = state;
35291             
35292         } else {
35293             mstate = this.ds.sortToggle;
35294         }
35295         //remove existing sort classes..
35296         
35297         var sc = this.sortClasses;
35298         var hds = this.el.select(this.headerSelector).removeClass(sc);
35299         
35300         for(var f in mstate) {
35301         
35302             var sortColumn = this.cm.findColumnIndex(f);
35303             
35304             if(sortColumn != -1){
35305                 var sortDir = mstate[f];        
35306                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
35307             }
35308         }
35309         
35310          
35311         
35312     },
35313
35314
35315     handleHeaderClick : function(g, index){
35316         if(this.headersDisabled){
35317             return;
35318         }
35319         var dm = g.dataSource, cm = g.colModel;
35320         if(!cm.isSortable(index)){
35321             return;
35322         }
35323         g.stopEditing();
35324         
35325         if (dm.multiSort) {
35326             // update the sortOrder
35327             var so = [];
35328             for(var i = 0; i < cm.config.length; i++ ) {
35329                 
35330                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
35331                     continue; // dont' bother, it's not in sort list or being set.
35332                 }
35333                 
35334                 so.push(cm.config[i].dataIndex);
35335             };
35336             dm.sortOrder = so;
35337         }
35338         
35339         
35340         dm.sort(cm.getDataIndex(index));
35341     },
35342
35343
35344     destroy : function(){
35345         if(this.colMenu){
35346             this.colMenu.removeAll();
35347             Roo.menu.MenuMgr.unregister(this.colMenu);
35348             this.colMenu.getEl().remove();
35349             delete this.colMenu;
35350         }
35351         if(this.hmenu){
35352             this.hmenu.removeAll();
35353             Roo.menu.MenuMgr.unregister(this.hmenu);
35354             this.hmenu.getEl().remove();
35355             delete this.hmenu;
35356         }
35357         if(this.grid.enableColumnMove){
35358             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35359             if(dds){
35360                 for(var dd in dds){
35361                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
35362                         var elid = dds[dd].dragElId;
35363                         dds[dd].unreg();
35364                         Roo.get(elid).remove();
35365                     } else if(dds[dd].config.isTarget){
35366                         dds[dd].proxyTop.remove();
35367                         dds[dd].proxyBottom.remove();
35368                         dds[dd].unreg();
35369                     }
35370                     if(Roo.dd.DDM.locationCache[dd]){
35371                         delete Roo.dd.DDM.locationCache[dd];
35372                     }
35373                 }
35374                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35375             }
35376         }
35377         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
35378         this.bind(null, null);
35379         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
35380     },
35381
35382     handleLockChange : function(){
35383         this.refresh(true);
35384     },
35385
35386     onDenyColumnLock : function(){
35387
35388     },
35389
35390     onDenyColumnHide : function(){
35391
35392     },
35393
35394     handleHdMenuClick : function(item){
35395         var index = this.hdCtxIndex;
35396         var cm = this.cm, ds = this.ds;
35397         switch(item.id){
35398             case "asc":
35399                 ds.sort(cm.getDataIndex(index), "ASC");
35400                 break;
35401             case "desc":
35402                 ds.sort(cm.getDataIndex(index), "DESC");
35403                 break;
35404             case "lock":
35405                 var lc = cm.getLockedCount();
35406                 if(cm.getColumnCount(true) <= lc+1){
35407                     this.onDenyColumnLock();
35408                     return;
35409                 }
35410                 if(lc != index){
35411                     cm.setLocked(index, true, true);
35412                     cm.moveColumn(index, lc);
35413                     this.grid.fireEvent("columnmove", index, lc);
35414                 }else{
35415                     cm.setLocked(index, true);
35416                 }
35417             break;
35418             case "unlock":
35419                 var lc = cm.getLockedCount();
35420                 if((lc-1) != index){
35421                     cm.setLocked(index, false, true);
35422                     cm.moveColumn(index, lc-1);
35423                     this.grid.fireEvent("columnmove", index, lc-1);
35424                 }else{
35425                     cm.setLocked(index, false);
35426                 }
35427             break;
35428             default:
35429                 index = cm.getIndexById(item.id.substr(4));
35430                 if(index != -1){
35431                     if(item.checked && cm.getColumnCount(true) <= 1){
35432                         this.onDenyColumnHide();
35433                         return false;
35434                     }
35435                     cm.setHidden(index, item.checked);
35436                 }
35437         }
35438         return true;
35439     },
35440
35441     beforeColMenuShow : function(){
35442         var cm = this.cm,  colCount = cm.getColumnCount();
35443         this.colMenu.removeAll();
35444         for(var i = 0; i < colCount; i++){
35445             this.colMenu.add(new Roo.menu.CheckItem({
35446                 id: "col-"+cm.getColumnId(i),
35447                 text: cm.getColumnHeader(i),
35448                 checked: !cm.isHidden(i),
35449                 hideOnClick:false
35450             }));
35451         }
35452     },
35453
35454     handleHdCtx : function(g, index, e){
35455         e.stopEvent();
35456         var hd = this.getHeaderCell(index);
35457         this.hdCtxIndex = index;
35458         var ms = this.hmenu.items, cm = this.cm;
35459         ms.get("asc").setDisabled(!cm.isSortable(index));
35460         ms.get("desc").setDisabled(!cm.isSortable(index));
35461         if(this.grid.enableColLock !== false){
35462             ms.get("lock").setDisabled(cm.isLocked(index));
35463             ms.get("unlock").setDisabled(!cm.isLocked(index));
35464         }
35465         this.hmenu.show(hd, "tl-bl");
35466     },
35467
35468     handleHdOver : function(e){
35469         var hd = this.findHeaderCell(e.getTarget());
35470         if(hd && !this.headersDisabled){
35471             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
35472                this.fly(hd).addClass("x-grid-hd-over");
35473             }
35474         }
35475     },
35476
35477     handleHdOut : function(e){
35478         var hd = this.findHeaderCell(e.getTarget());
35479         if(hd){
35480             this.fly(hd).removeClass("x-grid-hd-over");
35481         }
35482     },
35483
35484     handleSplitDblClick : function(e, t){
35485         var i = this.getCellIndex(t);
35486         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
35487             this.autoSizeColumn(i, true);
35488             this.layout();
35489         }
35490     },
35491
35492     render : function(){
35493
35494         var cm = this.cm;
35495         var colCount = cm.getColumnCount();
35496
35497         if(this.grid.monitorWindowResize === true){
35498             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35499         }
35500         var header = this.renderHeaders();
35501         var body = this.templates.body.apply({rows:""});
35502         var html = this.templates.master.apply({
35503             lockedBody: body,
35504             body: body,
35505             lockedHeader: header[0],
35506             header: header[1]
35507         });
35508
35509         //this.updateColumns();
35510
35511         this.grid.getGridEl().dom.innerHTML = html;
35512
35513         this.initElements();
35514         
35515         // a kludge to fix the random scolling effect in webkit
35516         this.el.on("scroll", function() {
35517             this.el.dom.scrollTop=0; // hopefully not recursive..
35518         },this);
35519
35520         this.scroller.on("scroll", this.handleScroll, this);
35521         this.lockedBody.on("mousewheel", this.handleWheel, this);
35522         this.mainBody.on("mousewheel", this.handleWheel, this);
35523
35524         this.mainHd.on("mouseover", this.handleHdOver, this);
35525         this.mainHd.on("mouseout", this.handleHdOut, this);
35526         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
35527                 {delegate: "."+this.splitClass});
35528
35529         this.lockedHd.on("mouseover", this.handleHdOver, this);
35530         this.lockedHd.on("mouseout", this.handleHdOut, this);
35531         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
35532                 {delegate: "."+this.splitClass});
35533
35534         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
35535             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35536         }
35537
35538         this.updateSplitters();
35539
35540         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
35541             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35542             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35543         }
35544
35545         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
35546             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
35547             this.hmenu.add(
35548                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
35549                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
35550             );
35551             if(this.grid.enableColLock !== false){
35552                 this.hmenu.add('-',
35553                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
35554                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
35555                 );
35556             }
35557             if(this.grid.enableColumnHide !== false){
35558
35559                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
35560                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
35561                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
35562
35563                 this.hmenu.add('-',
35564                     {id:"columns", text: this.columnsText, menu: this.colMenu}
35565                 );
35566             }
35567             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
35568
35569             this.grid.on("headercontextmenu", this.handleHdCtx, this);
35570         }
35571
35572         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
35573             this.dd = new Roo.grid.GridDragZone(this.grid, {
35574                 ddGroup : this.grid.ddGroup || 'GridDD'
35575             });
35576         }
35577
35578         /*
35579         for(var i = 0; i < colCount; i++){
35580             if(cm.isHidden(i)){
35581                 this.hideColumn(i);
35582             }
35583             if(cm.config[i].align){
35584                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
35585                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
35586             }
35587         }*/
35588         
35589         this.updateHeaderSortState();
35590
35591         this.beforeInitialResize();
35592         this.layout(true);
35593
35594         // two part rendering gives faster view to the user
35595         this.renderPhase2.defer(1, this);
35596     },
35597
35598     renderPhase2 : function(){
35599         // render the rows now
35600         this.refresh();
35601         if(this.grid.autoSizeColumns){
35602             this.autoSizeColumns();
35603         }
35604     },
35605
35606     beforeInitialResize : function(){
35607
35608     },
35609
35610     onColumnSplitterMoved : function(i, w){
35611         this.userResized = true;
35612         var cm = this.grid.colModel;
35613         cm.setColumnWidth(i, w, true);
35614         var cid = cm.getColumnId(i);
35615         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35616         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35617         this.updateSplitters();
35618         this.layout();
35619         this.grid.fireEvent("columnresize", i, w);
35620     },
35621
35622     syncRowHeights : function(startIndex, endIndex){
35623         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
35624             startIndex = startIndex || 0;
35625             var mrows = this.getBodyTable().rows;
35626             var lrows = this.getLockedTable().rows;
35627             var len = mrows.length-1;
35628             endIndex = Math.min(endIndex || len, len);
35629             for(var i = startIndex; i <= endIndex; i++){
35630                 var m = mrows[i], l = lrows[i];
35631                 var h = Math.max(m.offsetHeight, l.offsetHeight);
35632                 m.style.height = l.style.height = h + "px";
35633             }
35634         }
35635     },
35636
35637     layout : function(initialRender, is2ndPass){
35638         var g = this.grid;
35639         var auto = g.autoHeight;
35640         var scrollOffset = 16;
35641         var c = g.getGridEl(), cm = this.cm,
35642                 expandCol = g.autoExpandColumn,
35643                 gv = this;
35644         //c.beginMeasure();
35645
35646         if(!c.dom.offsetWidth){ // display:none?
35647             if(initialRender){
35648                 this.lockedWrap.show();
35649                 this.mainWrap.show();
35650             }
35651             return;
35652         }
35653
35654         var hasLock = this.cm.isLocked(0);
35655
35656         var tbh = this.headerPanel.getHeight();
35657         var bbh = this.footerPanel.getHeight();
35658
35659         if(auto){
35660             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
35661             var newHeight = ch + c.getBorderWidth("tb");
35662             if(g.maxHeight){
35663                 newHeight = Math.min(g.maxHeight, newHeight);
35664             }
35665             c.setHeight(newHeight);
35666         }
35667
35668         if(g.autoWidth){
35669             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
35670         }
35671
35672         var s = this.scroller;
35673
35674         var csize = c.getSize(true);
35675
35676         this.el.setSize(csize.width, csize.height);
35677
35678         this.headerPanel.setWidth(csize.width);
35679         this.footerPanel.setWidth(csize.width);
35680
35681         var hdHeight = this.mainHd.getHeight();
35682         var vw = csize.width;
35683         var vh = csize.height - (tbh + bbh);
35684
35685         s.setSize(vw, vh);
35686
35687         var bt = this.getBodyTable();
35688         var ltWidth = hasLock ?
35689                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
35690
35691         var scrollHeight = bt.offsetHeight;
35692         var scrollWidth = ltWidth + bt.offsetWidth;
35693         var vscroll = false, hscroll = false;
35694
35695         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
35696
35697         var lw = this.lockedWrap, mw = this.mainWrap;
35698         var lb = this.lockedBody, mb = this.mainBody;
35699
35700         setTimeout(function(){
35701             var t = s.dom.offsetTop;
35702             var w = s.dom.clientWidth,
35703                 h = s.dom.clientHeight;
35704
35705             lw.setTop(t);
35706             lw.setSize(ltWidth, h);
35707
35708             mw.setLeftTop(ltWidth, t);
35709             mw.setSize(w-ltWidth, h);
35710
35711             lb.setHeight(h-hdHeight);
35712             mb.setHeight(h-hdHeight);
35713
35714             if(is2ndPass !== true && !gv.userResized && expandCol){
35715                 // high speed resize without full column calculation
35716                 
35717                 var ci = cm.getIndexById(expandCol);
35718                 if (ci < 0) {
35719                     ci = cm.findColumnIndex(expandCol);
35720                 }
35721                 ci = Math.max(0, ci); // make sure it's got at least the first col.
35722                 var expandId = cm.getColumnId(ci);
35723                 var  tw = cm.getTotalWidth(false);
35724                 var currentWidth = cm.getColumnWidth(ci);
35725                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
35726                 if(currentWidth != cw){
35727                     cm.setColumnWidth(ci, cw, true);
35728                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35729                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35730                     gv.updateSplitters();
35731                     gv.layout(false, true);
35732                 }
35733             }
35734
35735             if(initialRender){
35736                 lw.show();
35737                 mw.show();
35738             }
35739             //c.endMeasure();
35740         }, 10);
35741     },
35742
35743     onWindowResize : function(){
35744         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
35745             return;
35746         }
35747         this.layout();
35748     },
35749
35750     appendFooter : function(parentEl){
35751         return null;
35752     },
35753
35754     sortAscText : "Sort Ascending",
35755     sortDescText : "Sort Descending",
35756     lockText : "Lock Column",
35757     unlockText : "Unlock Column",
35758     columnsText : "Columns"
35759 });
35760
35761
35762 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35763     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35764     this.proxy.el.addClass('x-grid3-col-dd');
35765 };
35766
35767 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35768     handleMouseDown : function(e){
35769
35770     },
35771
35772     callHandleMouseDown : function(e){
35773         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35774     }
35775 });
35776 /*
35777  * Based on:
35778  * Ext JS Library 1.1.1
35779  * Copyright(c) 2006-2007, Ext JS, LLC.
35780  *
35781  * Originally Released Under LGPL - original licence link has changed is not relivant.
35782  *
35783  * Fork - LGPL
35784  * <script type="text/javascript">
35785  */
35786  
35787 // private
35788 // This is a support class used internally by the Grid components
35789 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35790     this.grid = grid;
35791     this.view = grid.getView();
35792     this.proxy = this.view.resizeProxy;
35793     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
35794         "gridSplitters" + this.grid.getGridEl().id, {
35795         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
35796     });
35797     this.setHandleElId(Roo.id(hd));
35798     this.setOuterHandleElId(Roo.id(hd2));
35799     this.scroll = false;
35800 };
35801 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35802     fly: Roo.Element.fly,
35803
35804     b4StartDrag : function(x, y){
35805         this.view.headersDisabled = true;
35806         this.proxy.setHeight(this.view.mainWrap.getHeight());
35807         var w = this.cm.getColumnWidth(this.cellIndex);
35808         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35809         this.resetConstraints();
35810         this.setXConstraint(minw, 1000);
35811         this.setYConstraint(0, 0);
35812         this.minX = x - minw;
35813         this.maxX = x + 1000;
35814         this.startPos = x;
35815         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35816     },
35817
35818
35819     handleMouseDown : function(e){
35820         ev = Roo.EventObject.setEvent(e);
35821         var t = this.fly(ev.getTarget());
35822         if(t.hasClass("x-grid-split")){
35823             this.cellIndex = this.view.getCellIndex(t.dom);
35824             this.split = t.dom;
35825             this.cm = this.grid.colModel;
35826             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35827                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35828             }
35829         }
35830     },
35831
35832     endDrag : function(e){
35833         this.view.headersDisabled = false;
35834         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35835         var diff = endX - this.startPos;
35836         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
35837     },
35838
35839     autoOffset : function(){
35840         this.setDelta(0,0);
35841     }
35842 });/*
35843  * Based on:
35844  * Ext JS Library 1.1.1
35845  * Copyright(c) 2006-2007, Ext JS, LLC.
35846  *
35847  * Originally Released Under LGPL - original licence link has changed is not relivant.
35848  *
35849  * Fork - LGPL
35850  * <script type="text/javascript">
35851  */
35852  
35853 // private
35854 // This is a support class used internally by the Grid components
35855 Roo.grid.GridDragZone = function(grid, config){
35856     this.view = grid.getView();
35857     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35858     if(this.view.lockedBody){
35859         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35860         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35861     }
35862     this.scroll = false;
35863     this.grid = grid;
35864     this.ddel = document.createElement('div');
35865     this.ddel.className = 'x-grid-dd-wrap';
35866 };
35867
35868 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35869     ddGroup : "GridDD",
35870
35871     getDragData : function(e){
35872         var t = Roo.lib.Event.getTarget(e);
35873         var rowIndex = this.view.findRowIndex(t);
35874         if(rowIndex !== false){
35875             var sm = this.grid.selModel;
35876             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35877               //  sm.mouseDown(e, t);
35878             //}
35879             if (e.hasModifier()){
35880                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35881             }
35882             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
35883         }
35884         return false;
35885     },
35886
35887     onInitDrag : function(e){
35888         var data = this.dragData;
35889         this.ddel.innerHTML = this.grid.getDragDropText();
35890         this.proxy.update(this.ddel);
35891         // fire start drag?
35892     },
35893
35894     afterRepair : function(){
35895         this.dragging = false;
35896     },
35897
35898     getRepairXY : function(e, data){
35899         return false;
35900     },
35901
35902     onEndDrag : function(data, e){
35903         // fire end drag?
35904     },
35905
35906     onValidDrop : function(dd, e, id){
35907         // fire drag drop?
35908         this.hideProxy();
35909     },
35910
35911     beforeInvalidDrop : function(e, id){
35912
35913     }
35914 });/*
35915  * Based on:
35916  * Ext JS Library 1.1.1
35917  * Copyright(c) 2006-2007, Ext JS, LLC.
35918  *
35919  * Originally Released Under LGPL - original licence link has changed is not relivant.
35920  *
35921  * Fork - LGPL
35922  * <script type="text/javascript">
35923  */
35924  
35925
35926 /**
35927  * @class Roo.grid.ColumnModel
35928  * @extends Roo.util.Observable
35929  * This is the default implementation of a ColumnModel used by the Grid. It defines
35930  * the columns in the grid.
35931  * <br>Usage:<br>
35932  <pre><code>
35933  var colModel = new Roo.grid.ColumnModel([
35934         {header: "Ticker", width: 60, sortable: true, locked: true},
35935         {header: "Company Name", width: 150, sortable: true},
35936         {header: "Market Cap.", width: 100, sortable: true},
35937         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35938         {header: "Employees", width: 100, sortable: true, resizable: false}
35939  ]);
35940  </code></pre>
35941  * <p>
35942  
35943  * The config options listed for this class are options which may appear in each
35944  * individual column definition.
35945  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35946  * @constructor
35947  * @param {Object} config An Array of column config objects. See this class's
35948  * config objects for details.
35949 */
35950 Roo.grid.ColumnModel = function(config){
35951         /**
35952      * The config passed into the constructor
35953      */
35954     this.config = config;
35955     this.lookup = {};
35956
35957     // if no id, create one
35958     // if the column does not have a dataIndex mapping,
35959     // map it to the order it is in the config
35960     for(var i = 0, len = config.length; i < len; i++){
35961         var c = config[i];
35962         if(typeof c.dataIndex == "undefined"){
35963             c.dataIndex = i;
35964         }
35965         if(typeof c.renderer == "string"){
35966             c.renderer = Roo.util.Format[c.renderer];
35967         }
35968         if(typeof c.id == "undefined"){
35969             c.id = Roo.id();
35970         }
35971         if(c.editor && c.editor.xtype){
35972             c.editor  = Roo.factory(c.editor, Roo.grid);
35973         }
35974         if(c.editor && c.editor.isFormField){
35975             c.editor = new Roo.grid.GridEditor(c.editor);
35976         }
35977         this.lookup[c.id] = c;
35978     }
35979
35980     /**
35981      * The width of columns which have no width specified (defaults to 100)
35982      * @type Number
35983      */
35984     this.defaultWidth = 100;
35985
35986     /**
35987      * Default sortable of columns which have no sortable specified (defaults to false)
35988      * @type Boolean
35989      */
35990     this.defaultSortable = false;
35991
35992     this.addEvents({
35993         /**
35994              * @event widthchange
35995              * Fires when the width of a column changes.
35996              * @param {ColumnModel} this
35997              * @param {Number} columnIndex The column index
35998              * @param {Number} newWidth The new width
35999              */
36000             "widthchange": true,
36001         /**
36002              * @event headerchange
36003              * Fires when the text of a header changes.
36004              * @param {ColumnModel} this
36005              * @param {Number} columnIndex The column index
36006              * @param {Number} newText The new header text
36007              */
36008             "headerchange": true,
36009         /**
36010              * @event hiddenchange
36011              * Fires when a column is hidden or "unhidden".
36012              * @param {ColumnModel} this
36013              * @param {Number} columnIndex The column index
36014              * @param {Boolean} hidden true if hidden, false otherwise
36015              */
36016             "hiddenchange": true,
36017             /**
36018          * @event columnmoved
36019          * Fires when a column is moved.
36020          * @param {ColumnModel} this
36021          * @param {Number} oldIndex
36022          * @param {Number} newIndex
36023          */
36024         "columnmoved" : true,
36025         /**
36026          * @event columlockchange
36027          * Fires when a column's locked state is changed
36028          * @param {ColumnModel} this
36029          * @param {Number} colIndex
36030          * @param {Boolean} locked true if locked
36031          */
36032         "columnlockchange" : true
36033     });
36034     Roo.grid.ColumnModel.superclass.constructor.call(this);
36035 };
36036 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
36037     /**
36038      * @cfg {String} header The header text to display in the Grid view.
36039      */
36040     /**
36041      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
36042      * {@link Roo.data.Record} definition from which to draw the column's value. If not
36043      * specified, the column's index is used as an index into the Record's data Array.
36044      */
36045     /**
36046      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
36047      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
36048      */
36049     /**
36050      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
36051      * Defaults to the value of the {@link #defaultSortable} property.
36052      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
36053      */
36054     /**
36055      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
36056      */
36057     /**
36058      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
36059      */
36060     /**
36061      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
36062      */
36063     /**
36064      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
36065      */
36066     /**
36067      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
36068      * given the cell's data value. See {@link #setRenderer}. If not specified, the
36069      * default renderer uses the raw data value.
36070      */
36071        /**
36072      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
36073      */
36074     /**
36075      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
36076      */
36077
36078     /**
36079      * Returns the id of the column at the specified index.
36080      * @param {Number} index The column index
36081      * @return {String} the id
36082      */
36083     getColumnId : function(index){
36084         return this.config[index].id;
36085     },
36086
36087     /**
36088      * Returns the column for a specified id.
36089      * @param {String} id The column id
36090      * @return {Object} the column
36091      */
36092     getColumnById : function(id){
36093         return this.lookup[id];
36094     },
36095
36096     
36097     /**
36098      * Returns the column for a specified dataIndex.
36099      * @param {String} dataIndex The column dataIndex
36100      * @return {Object|Boolean} the column or false if not found
36101      */
36102     getColumnByDataIndex: function(dataIndex){
36103         var index = this.findColumnIndex(dataIndex);
36104         return index > -1 ? this.config[index] : false;
36105     },
36106     
36107     /**
36108      * Returns the index for a specified column id.
36109      * @param {String} id The column id
36110      * @return {Number} the index, or -1 if not found
36111      */
36112     getIndexById : function(id){
36113         for(var i = 0, len = this.config.length; i < len; i++){
36114             if(this.config[i].id == id){
36115                 return i;
36116             }
36117         }
36118         return -1;
36119     },
36120     
36121     /**
36122      * Returns the index for a specified column dataIndex.
36123      * @param {String} dataIndex The column dataIndex
36124      * @return {Number} the index, or -1 if not found
36125      */
36126     
36127     findColumnIndex : function(dataIndex){
36128         for(var i = 0, len = this.config.length; i < len; i++){
36129             if(this.config[i].dataIndex == dataIndex){
36130                 return i;
36131             }
36132         }
36133         return -1;
36134     },
36135     
36136     
36137     moveColumn : function(oldIndex, newIndex){
36138         var c = this.config[oldIndex];
36139         this.config.splice(oldIndex, 1);
36140         this.config.splice(newIndex, 0, c);
36141         this.dataMap = null;
36142         this.fireEvent("columnmoved", this, oldIndex, newIndex);
36143     },
36144
36145     isLocked : function(colIndex){
36146         return this.config[colIndex].locked === true;
36147     },
36148
36149     setLocked : function(colIndex, value, suppressEvent){
36150         if(this.isLocked(colIndex) == value){
36151             return;
36152         }
36153         this.config[colIndex].locked = value;
36154         if(!suppressEvent){
36155             this.fireEvent("columnlockchange", this, colIndex, value);
36156         }
36157     },
36158
36159     getTotalLockedWidth : function(){
36160         var totalWidth = 0;
36161         for(var i = 0; i < this.config.length; i++){
36162             if(this.isLocked(i) && !this.isHidden(i)){
36163                 this.totalWidth += this.getColumnWidth(i);
36164             }
36165         }
36166         return totalWidth;
36167     },
36168
36169     getLockedCount : function(){
36170         for(var i = 0, len = this.config.length; i < len; i++){
36171             if(!this.isLocked(i)){
36172                 return i;
36173             }
36174         }
36175     },
36176
36177     /**
36178      * Returns the number of columns.
36179      * @return {Number}
36180      */
36181     getColumnCount : function(visibleOnly){
36182         if(visibleOnly === true){
36183             var c = 0;
36184             for(var i = 0, len = this.config.length; i < len; i++){
36185                 if(!this.isHidden(i)){
36186                     c++;
36187                 }
36188             }
36189             return c;
36190         }
36191         return this.config.length;
36192     },
36193
36194     /**
36195      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
36196      * @param {Function} fn
36197      * @param {Object} scope (optional)
36198      * @return {Array} result
36199      */
36200     getColumnsBy : function(fn, scope){
36201         var r = [];
36202         for(var i = 0, len = this.config.length; i < len; i++){
36203             var c = this.config[i];
36204             if(fn.call(scope||this, c, i) === true){
36205                 r[r.length] = c;
36206             }
36207         }
36208         return r;
36209     },
36210
36211     /**
36212      * Returns true if the specified column is sortable.
36213      * @param {Number} col The column index
36214      * @return {Boolean}
36215      */
36216     isSortable : function(col){
36217         if(typeof this.config[col].sortable == "undefined"){
36218             return this.defaultSortable;
36219         }
36220         return this.config[col].sortable;
36221     },
36222
36223     /**
36224      * Returns the rendering (formatting) function defined for the column.
36225      * @param {Number} col The column index.
36226      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
36227      */
36228     getRenderer : function(col){
36229         if(!this.config[col].renderer){
36230             return Roo.grid.ColumnModel.defaultRenderer;
36231         }
36232         return this.config[col].renderer;
36233     },
36234
36235     /**
36236      * Sets the rendering (formatting) function for a column.
36237      * @param {Number} col The column index
36238      * @param {Function} fn The function to use to process the cell's raw data
36239      * to return HTML markup for the grid view. The render function is called with
36240      * the following parameters:<ul>
36241      * <li>Data value.</li>
36242      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
36243      * <li>css A CSS style string to apply to the table cell.</li>
36244      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
36245      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
36246      * <li>Row index</li>
36247      * <li>Column index</li>
36248      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
36249      */
36250     setRenderer : function(col, fn){
36251         this.config[col].renderer = fn;
36252     },
36253
36254     /**
36255      * Returns the width for the specified column.
36256      * @param {Number} col The column index
36257      * @return {Number}
36258      */
36259     getColumnWidth : function(col){
36260         return this.config[col].width * 1 || this.defaultWidth;
36261     },
36262
36263     /**
36264      * Sets the width for a column.
36265      * @param {Number} col The column index
36266      * @param {Number} width The new width
36267      */
36268     setColumnWidth : function(col, width, suppressEvent){
36269         this.config[col].width = width;
36270         this.totalWidth = null;
36271         if(!suppressEvent){
36272              this.fireEvent("widthchange", this, col, width);
36273         }
36274     },
36275
36276     /**
36277      * Returns the total width of all columns.
36278      * @param {Boolean} includeHidden True to include hidden column widths
36279      * @return {Number}
36280      */
36281     getTotalWidth : function(includeHidden){
36282         if(!this.totalWidth){
36283             this.totalWidth = 0;
36284             for(var i = 0, len = this.config.length; i < len; i++){
36285                 if(includeHidden || !this.isHidden(i)){
36286                     this.totalWidth += this.getColumnWidth(i);
36287                 }
36288             }
36289         }
36290         return this.totalWidth;
36291     },
36292
36293     /**
36294      * Returns the header for the specified column.
36295      * @param {Number} col The column index
36296      * @return {String}
36297      */
36298     getColumnHeader : function(col){
36299         return this.config[col].header;
36300     },
36301
36302     /**
36303      * Sets the header for a column.
36304      * @param {Number} col The column index
36305      * @param {String} header The new header
36306      */
36307     setColumnHeader : function(col, header){
36308         this.config[col].header = header;
36309         this.fireEvent("headerchange", this, col, header);
36310     },
36311
36312     /**
36313      * Returns the tooltip for the specified column.
36314      * @param {Number} col The column index
36315      * @return {String}
36316      */
36317     getColumnTooltip : function(col){
36318             return this.config[col].tooltip;
36319     },
36320     /**
36321      * Sets the tooltip for a column.
36322      * @param {Number} col The column index
36323      * @param {String} tooltip The new tooltip
36324      */
36325     setColumnTooltip : function(col, tooltip){
36326             this.config[col].tooltip = tooltip;
36327     },
36328
36329     /**
36330      * Returns the dataIndex for the specified column.
36331      * @param {Number} col The column index
36332      * @return {Number}
36333      */
36334     getDataIndex : function(col){
36335         return this.config[col].dataIndex;
36336     },
36337
36338     /**
36339      * Sets the dataIndex for a column.
36340      * @param {Number} col The column index
36341      * @param {Number} dataIndex The new dataIndex
36342      */
36343     setDataIndex : function(col, dataIndex){
36344         this.config[col].dataIndex = dataIndex;
36345     },
36346
36347     
36348     
36349     /**
36350      * Returns true if the cell is editable.
36351      * @param {Number} colIndex The column index
36352      * @param {Number} rowIndex The row index
36353      * @return {Boolean}
36354      */
36355     isCellEditable : function(colIndex, rowIndex){
36356         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
36357     },
36358
36359     /**
36360      * Returns the editor defined for the cell/column.
36361      * return false or null to disable editing.
36362      * @param {Number} colIndex The column index
36363      * @param {Number} rowIndex The row index
36364      * @return {Object}
36365      */
36366     getCellEditor : function(colIndex, rowIndex){
36367         return this.config[colIndex].editor;
36368     },
36369
36370     /**
36371      * Sets if a column is editable.
36372      * @param {Number} col The column index
36373      * @param {Boolean} editable True if the column is editable
36374      */
36375     setEditable : function(col, editable){
36376         this.config[col].editable = editable;
36377     },
36378
36379
36380     /**
36381      * Returns true if the column is hidden.
36382      * @param {Number} colIndex The column index
36383      * @return {Boolean}
36384      */
36385     isHidden : function(colIndex){
36386         return this.config[colIndex].hidden;
36387     },
36388
36389
36390     /**
36391      * Returns true if the column width cannot be changed
36392      */
36393     isFixed : function(colIndex){
36394         return this.config[colIndex].fixed;
36395     },
36396
36397     /**
36398      * Returns true if the column can be resized
36399      * @return {Boolean}
36400      */
36401     isResizable : function(colIndex){
36402         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
36403     },
36404     /**
36405      * Sets if a column is hidden.
36406      * @param {Number} colIndex The column index
36407      * @param {Boolean} hidden True if the column is hidden
36408      */
36409     setHidden : function(colIndex, hidden){
36410         this.config[colIndex].hidden = hidden;
36411         this.totalWidth = null;
36412         this.fireEvent("hiddenchange", this, colIndex, hidden);
36413     },
36414
36415     /**
36416      * Sets the editor for a column.
36417      * @param {Number} col The column index
36418      * @param {Object} editor The editor object
36419      */
36420     setEditor : function(col, editor){
36421         this.config[col].editor = editor;
36422     }
36423 });
36424
36425 Roo.grid.ColumnModel.defaultRenderer = function(value){
36426         if(typeof value == "string" && value.length < 1){
36427             return "&#160;";
36428         }
36429         return value;
36430 };
36431
36432 // Alias for backwards compatibility
36433 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
36434 /*
36435  * Based on:
36436  * Ext JS Library 1.1.1
36437  * Copyright(c) 2006-2007, Ext JS, LLC.
36438  *
36439  * Originally Released Under LGPL - original licence link has changed is not relivant.
36440  *
36441  * Fork - LGPL
36442  * <script type="text/javascript">
36443  */
36444
36445 /**
36446  * @class Roo.grid.AbstractSelectionModel
36447  * @extends Roo.util.Observable
36448  * Abstract base class for grid SelectionModels.  It provides the interface that should be
36449  * implemented by descendant classes.  This class should not be directly instantiated.
36450  * @constructor
36451  */
36452 Roo.grid.AbstractSelectionModel = function(){
36453     this.locked = false;
36454     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
36455 };
36456
36457 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
36458     /** @ignore Called by the grid automatically. Do not call directly. */
36459     init : function(grid){
36460         this.grid = grid;
36461         this.initEvents();
36462     },
36463
36464     /**
36465      * Locks the selections.
36466      */
36467     lock : function(){
36468         this.locked = true;
36469     },
36470
36471     /**
36472      * Unlocks the selections.
36473      */
36474     unlock : function(){
36475         this.locked = false;
36476     },
36477
36478     /**
36479      * Returns true if the selections are locked.
36480      * @return {Boolean}
36481      */
36482     isLocked : function(){
36483         return this.locked;
36484     }
36485 });/*
36486  * Based on:
36487  * Ext JS Library 1.1.1
36488  * Copyright(c) 2006-2007, Ext JS, LLC.
36489  *
36490  * Originally Released Under LGPL - original licence link has changed is not relivant.
36491  *
36492  * Fork - LGPL
36493  * <script type="text/javascript">
36494  */
36495 /**
36496  * @extends Roo.grid.AbstractSelectionModel
36497  * @class Roo.grid.RowSelectionModel
36498  * The default SelectionModel used by {@link Roo.grid.Grid}.
36499  * It supports multiple selections and keyboard selection/navigation. 
36500  * @constructor
36501  * @param {Object} config
36502  */
36503 Roo.grid.RowSelectionModel = function(config){
36504     Roo.apply(this, config);
36505     this.selections = new Roo.util.MixedCollection(false, function(o){
36506         return o.id;
36507     });
36508
36509     this.last = false;
36510     this.lastActive = false;
36511
36512     this.addEvents({
36513         /**
36514              * @event selectionchange
36515              * Fires when the selection changes
36516              * @param {SelectionModel} this
36517              */
36518             "selectionchange" : true,
36519         /**
36520              * @event afterselectionchange
36521              * Fires after the selection changes (eg. by key press or clicking)
36522              * @param {SelectionModel} this
36523              */
36524             "afterselectionchange" : true,
36525         /**
36526              * @event beforerowselect
36527              * Fires when a row is selected being selected, return false to cancel.
36528              * @param {SelectionModel} this
36529              * @param {Number} rowIndex The selected index
36530              * @param {Boolean} keepExisting False if other selections will be cleared
36531              */
36532             "beforerowselect" : true,
36533         /**
36534              * @event rowselect
36535              * Fires when a row is selected.
36536              * @param {SelectionModel} this
36537              * @param {Number} rowIndex The selected index
36538              * @param {Roo.data.Record} r The record
36539              */
36540             "rowselect" : true,
36541         /**
36542              * @event rowdeselect
36543              * Fires when a row is deselected.
36544              * @param {SelectionModel} this
36545              * @param {Number} rowIndex The selected index
36546              */
36547         "rowdeselect" : true
36548     });
36549     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36550     this.locked = false;
36551 };
36552
36553 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
36554     /**
36555      * @cfg {Boolean} singleSelect
36556      * True to allow selection of only one row at a time (defaults to false)
36557      */
36558     singleSelect : false,
36559
36560     // private
36561     initEvents : function(){
36562
36563         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36564             this.grid.on("mousedown", this.handleMouseDown, this);
36565         }else{ // allow click to work like normal
36566             this.grid.on("rowclick", this.handleDragableRowClick, this);
36567         }
36568
36569         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36570             "up" : function(e){
36571                 if(!e.shiftKey){
36572                     this.selectPrevious(e.shiftKey);
36573                 }else if(this.last !== false && this.lastActive !== false){
36574                     var last = this.last;
36575                     this.selectRange(this.last,  this.lastActive-1);
36576                     this.grid.getView().focusRow(this.lastActive);
36577                     if(last !== false){
36578                         this.last = last;
36579                     }
36580                 }else{
36581                     this.selectFirstRow();
36582                 }
36583                 this.fireEvent("afterselectionchange", this);
36584             },
36585             "down" : function(e){
36586                 if(!e.shiftKey){
36587                     this.selectNext(e.shiftKey);
36588                 }else if(this.last !== false && this.lastActive !== false){
36589                     var last = this.last;
36590                     this.selectRange(this.last,  this.lastActive+1);
36591                     this.grid.getView().focusRow(this.lastActive);
36592                     if(last !== false){
36593                         this.last = last;
36594                     }
36595                 }else{
36596                     this.selectFirstRow();
36597                 }
36598                 this.fireEvent("afterselectionchange", this);
36599             },
36600             scope: this
36601         });
36602
36603         var view = this.grid.view;
36604         view.on("refresh", this.onRefresh, this);
36605         view.on("rowupdated", this.onRowUpdated, this);
36606         view.on("rowremoved", this.onRemove, this);
36607     },
36608
36609     // private
36610     onRefresh : function(){
36611         var ds = this.grid.dataSource, i, v = this.grid.view;
36612         var s = this.selections;
36613         s.each(function(r){
36614             if((i = ds.indexOfId(r.id)) != -1){
36615                 v.onRowSelect(i);
36616             }else{
36617                 s.remove(r);
36618             }
36619         });
36620     },
36621
36622     // private
36623     onRemove : function(v, index, r){
36624         this.selections.remove(r);
36625     },
36626
36627     // private
36628     onRowUpdated : function(v, index, r){
36629         if(this.isSelected(r)){
36630             v.onRowSelect(index);
36631         }
36632     },
36633
36634     /**
36635      * Select records.
36636      * @param {Array} records The records to select
36637      * @param {Boolean} keepExisting (optional) True to keep existing selections
36638      */
36639     selectRecords : function(records, keepExisting){
36640         if(!keepExisting){
36641             this.clearSelections();
36642         }
36643         var ds = this.grid.dataSource;
36644         for(var i = 0, len = records.length; i < len; i++){
36645             this.selectRow(ds.indexOf(records[i]), true);
36646         }
36647     },
36648
36649     /**
36650      * Gets the number of selected rows.
36651      * @return {Number}
36652      */
36653     getCount : function(){
36654         return this.selections.length;
36655     },
36656
36657     /**
36658      * Selects the first row in the grid.
36659      */
36660     selectFirstRow : function(){
36661         this.selectRow(0);
36662     },
36663
36664     /**
36665      * Select the last row.
36666      * @param {Boolean} keepExisting (optional) True to keep existing selections
36667      */
36668     selectLastRow : function(keepExisting){
36669         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
36670     },
36671
36672     /**
36673      * Selects the row immediately following the last selected row.
36674      * @param {Boolean} keepExisting (optional) True to keep existing selections
36675      */
36676     selectNext : function(keepExisting){
36677         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
36678             this.selectRow(this.last+1, keepExisting);
36679             this.grid.getView().focusRow(this.last);
36680         }
36681     },
36682
36683     /**
36684      * Selects the row that precedes the last selected row.
36685      * @param {Boolean} keepExisting (optional) True to keep existing selections
36686      */
36687     selectPrevious : function(keepExisting){
36688         if(this.last){
36689             this.selectRow(this.last-1, keepExisting);
36690             this.grid.getView().focusRow(this.last);
36691         }
36692     },
36693
36694     /**
36695      * Returns the selected records
36696      * @return {Array} Array of selected records
36697      */
36698     getSelections : function(){
36699         return [].concat(this.selections.items);
36700     },
36701
36702     /**
36703      * Returns the first selected record.
36704      * @return {Record}
36705      */
36706     getSelected : function(){
36707         return this.selections.itemAt(0);
36708     },
36709
36710
36711     /**
36712      * Clears all selections.
36713      */
36714     clearSelections : function(fast){
36715         if(this.locked) return;
36716         if(fast !== true){
36717             var ds = this.grid.dataSource;
36718             var s = this.selections;
36719             s.each(function(r){
36720                 this.deselectRow(ds.indexOfId(r.id));
36721             }, this);
36722             s.clear();
36723         }else{
36724             this.selections.clear();
36725         }
36726         this.last = false;
36727     },
36728
36729
36730     /**
36731      * Selects all rows.
36732      */
36733     selectAll : function(){
36734         if(this.locked) return;
36735         this.selections.clear();
36736         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
36737             this.selectRow(i, true);
36738         }
36739     },
36740
36741     /**
36742      * Returns True if there is a selection.
36743      * @return {Boolean}
36744      */
36745     hasSelection : function(){
36746         return this.selections.length > 0;
36747     },
36748
36749     /**
36750      * Returns True if the specified row is selected.
36751      * @param {Number/Record} record The record or index of the record to check
36752      * @return {Boolean}
36753      */
36754     isSelected : function(index){
36755         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
36756         return (r && this.selections.key(r.id) ? true : false);
36757     },
36758
36759     /**
36760      * Returns True if the specified record id is selected.
36761      * @param {String} id The id of record to check
36762      * @return {Boolean}
36763      */
36764     isIdSelected : function(id){
36765         return (this.selections.key(id) ? true : false);
36766     },
36767
36768     // private
36769     handleMouseDown : function(e, t){
36770         var view = this.grid.getView(), rowIndex;
36771         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36772             return;
36773         };
36774         if(e.shiftKey && this.last !== false){
36775             var last = this.last;
36776             this.selectRange(last, rowIndex, e.ctrlKey);
36777             this.last = last; // reset the last
36778             view.focusRow(rowIndex);
36779         }else{
36780             var isSelected = this.isSelected(rowIndex);
36781             if(e.button !== 0 && isSelected){
36782                 view.focusRow(rowIndex);
36783             }else if(e.ctrlKey && isSelected){
36784                 this.deselectRow(rowIndex);
36785             }else if(!isSelected){
36786                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36787                 view.focusRow(rowIndex);
36788             }
36789         }
36790         this.fireEvent("afterselectionchange", this);
36791     },
36792     // private
36793     handleDragableRowClick :  function(grid, rowIndex, e) 
36794     {
36795         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36796             this.selectRow(rowIndex, false);
36797             grid.view.focusRow(rowIndex);
36798              this.fireEvent("afterselectionchange", this);
36799         }
36800     },
36801     
36802     /**
36803      * Selects multiple rows.
36804      * @param {Array} rows Array of the indexes of the row to select
36805      * @param {Boolean} keepExisting (optional) True to keep existing selections
36806      */
36807     selectRows : function(rows, keepExisting){
36808         if(!keepExisting){
36809             this.clearSelections();
36810         }
36811         for(var i = 0, len = rows.length; i < len; i++){
36812             this.selectRow(rows[i], true);
36813         }
36814     },
36815
36816     /**
36817      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36818      * @param {Number} startRow The index of the first row in the range
36819      * @param {Number} endRow The index of the last row in the range
36820      * @param {Boolean} keepExisting (optional) True to retain existing selections
36821      */
36822     selectRange : function(startRow, endRow, keepExisting){
36823         if(this.locked) return;
36824         if(!keepExisting){
36825             this.clearSelections();
36826         }
36827         if(startRow <= endRow){
36828             for(var i = startRow; i <= endRow; i++){
36829                 this.selectRow(i, true);
36830             }
36831         }else{
36832             for(var i = startRow; i >= endRow; i--){
36833                 this.selectRow(i, true);
36834             }
36835         }
36836     },
36837
36838     /**
36839      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36840      * @param {Number} startRow The index of the first row in the range
36841      * @param {Number} endRow The index of the last row in the range
36842      */
36843     deselectRange : function(startRow, endRow, preventViewNotify){
36844         if(this.locked) return;
36845         for(var i = startRow; i <= endRow; i++){
36846             this.deselectRow(i, preventViewNotify);
36847         }
36848     },
36849
36850     /**
36851      * Selects a row.
36852      * @param {Number} row The index of the row to select
36853      * @param {Boolean} keepExisting (optional) True to keep existing selections
36854      */
36855     selectRow : function(index, keepExisting, preventViewNotify){
36856         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
36857         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36858             if(!keepExisting || this.singleSelect){
36859                 this.clearSelections();
36860             }
36861             var r = this.grid.dataSource.getAt(index);
36862             this.selections.add(r);
36863             this.last = this.lastActive = index;
36864             if(!preventViewNotify){
36865                 this.grid.getView().onRowSelect(index);
36866             }
36867             this.fireEvent("rowselect", this, index, r);
36868             this.fireEvent("selectionchange", this);
36869         }
36870     },
36871
36872     /**
36873      * Deselects a row.
36874      * @param {Number} row The index of the row to deselect
36875      */
36876     deselectRow : function(index, preventViewNotify){
36877         if(this.locked) return;
36878         if(this.last == index){
36879             this.last = false;
36880         }
36881         if(this.lastActive == index){
36882             this.lastActive = false;
36883         }
36884         var r = this.grid.dataSource.getAt(index);
36885         this.selections.remove(r);
36886         if(!preventViewNotify){
36887             this.grid.getView().onRowDeselect(index);
36888         }
36889         this.fireEvent("rowdeselect", this, index);
36890         this.fireEvent("selectionchange", this);
36891     },
36892
36893     // private
36894     restoreLast : function(){
36895         if(this._last){
36896             this.last = this._last;
36897         }
36898     },
36899
36900     // private
36901     acceptsNav : function(row, col, cm){
36902         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36903     },
36904
36905     // private
36906     onEditorKey : function(field, e){
36907         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36908         if(k == e.TAB){
36909             e.stopEvent();
36910             ed.completeEdit();
36911             if(e.shiftKey){
36912                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36913             }else{
36914                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36915             }
36916         }else if(k == e.ENTER && !e.ctrlKey){
36917             e.stopEvent();
36918             ed.completeEdit();
36919             if(e.shiftKey){
36920                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36921             }else{
36922                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36923             }
36924         }else if(k == e.ESC){
36925             ed.cancelEdit();
36926         }
36927         if(newCell){
36928             g.startEditing(newCell[0], newCell[1]);
36929         }
36930     }
36931 });/*
36932  * Based on:
36933  * Ext JS Library 1.1.1
36934  * Copyright(c) 2006-2007, Ext JS, LLC.
36935  *
36936  * Originally Released Under LGPL - original licence link has changed is not relivant.
36937  *
36938  * Fork - LGPL
36939  * <script type="text/javascript">
36940  */
36941 /**
36942  * @class Roo.grid.CellSelectionModel
36943  * @extends Roo.grid.AbstractSelectionModel
36944  * This class provides the basic implementation for cell selection in a grid.
36945  * @constructor
36946  * @param {Object} config The object containing the configuration of this model.
36947  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36948  */
36949 Roo.grid.CellSelectionModel = function(config){
36950     Roo.apply(this, config);
36951
36952     this.selection = null;
36953
36954     this.addEvents({
36955         /**
36956              * @event beforerowselect
36957              * Fires before a cell is selected.
36958              * @param {SelectionModel} this
36959              * @param {Number} rowIndex The selected row index
36960              * @param {Number} colIndex The selected cell index
36961              */
36962             "beforecellselect" : true,
36963         /**
36964              * @event cellselect
36965              * Fires when a cell is selected.
36966              * @param {SelectionModel} this
36967              * @param {Number} rowIndex The selected row index
36968              * @param {Number} colIndex The selected cell index
36969              */
36970             "cellselect" : true,
36971         /**
36972              * @event selectionchange
36973              * Fires when the active selection changes.
36974              * @param {SelectionModel} this
36975              * @param {Object} selection null for no selection or an object (o) with two properties
36976                 <ul>
36977                 <li>o.record: the record object for the row the selection is in</li>
36978                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36979                 </ul>
36980              */
36981             "selectionchange" : true,
36982         /**
36983              * @event tabend
36984              * Fires when the tab (or enter) was pressed on the last editable cell
36985              * You can use this to trigger add new row.
36986              * @param {SelectionModel} this
36987              */
36988             "tabend" : true,
36989          /**
36990              * @event beforeeditnext
36991              * Fires before the next editable sell is made active
36992              * You can use this to skip to another cell or fire the tabend
36993              *    if you set cell to false
36994              * @param {Object} eventdata object : { cell : [ row, col ] } 
36995              */
36996             "beforeeditnext" : true
36997     });
36998     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36999 };
37000
37001 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
37002     
37003     enter_is_tab: false,
37004
37005     /** @ignore */
37006     initEvents : function(){
37007         this.grid.on("mousedown", this.handleMouseDown, this);
37008         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
37009         var view = this.grid.view;
37010         view.on("refresh", this.onViewChange, this);
37011         view.on("rowupdated", this.onRowUpdated, this);
37012         view.on("beforerowremoved", this.clearSelections, this);
37013         view.on("beforerowsinserted", this.clearSelections, this);
37014         if(this.grid.isEditor){
37015             this.grid.on("beforeedit", this.beforeEdit,  this);
37016         }
37017     },
37018
37019         //private
37020     beforeEdit : function(e){
37021         this.select(e.row, e.column, false, true, e.record);
37022     },
37023
37024         //private
37025     onRowUpdated : function(v, index, r){
37026         if(this.selection && this.selection.record == r){
37027             v.onCellSelect(index, this.selection.cell[1]);
37028         }
37029     },
37030
37031         //private
37032     onViewChange : function(){
37033         this.clearSelections(true);
37034     },
37035
37036         /**
37037          * Returns the currently selected cell,.
37038          * @return {Array} The selected cell (row, column) or null if none selected.
37039          */
37040     getSelectedCell : function(){
37041         return this.selection ? this.selection.cell : null;
37042     },
37043
37044     /**
37045      * Clears all selections.
37046      * @param {Boolean} true to prevent the gridview from being notified about the change.
37047      */
37048     clearSelections : function(preventNotify){
37049         var s = this.selection;
37050         if(s){
37051             if(preventNotify !== true){
37052                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
37053             }
37054             this.selection = null;
37055             this.fireEvent("selectionchange", this, null);
37056         }
37057     },
37058
37059     /**
37060      * Returns true if there is a selection.
37061      * @return {Boolean}
37062      */
37063     hasSelection : function(){
37064         return this.selection ? true : false;
37065     },
37066
37067     /** @ignore */
37068     handleMouseDown : function(e, t){
37069         var v = this.grid.getView();
37070         if(this.isLocked()){
37071             return;
37072         };
37073         var row = v.findRowIndex(t);
37074         var cell = v.findCellIndex(t);
37075         if(row !== false && cell !== false){
37076             this.select(row, cell);
37077         }
37078     },
37079
37080     /**
37081      * Selects a cell.
37082      * @param {Number} rowIndex
37083      * @param {Number} collIndex
37084      */
37085     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
37086         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
37087             this.clearSelections();
37088             r = r || this.grid.dataSource.getAt(rowIndex);
37089             this.selection = {
37090                 record : r,
37091                 cell : [rowIndex, colIndex]
37092             };
37093             if(!preventViewNotify){
37094                 var v = this.grid.getView();
37095                 v.onCellSelect(rowIndex, colIndex);
37096                 if(preventFocus !== true){
37097                     v.focusCell(rowIndex, colIndex);
37098                 }
37099             }
37100             this.fireEvent("cellselect", this, rowIndex, colIndex);
37101             this.fireEvent("selectionchange", this, this.selection);
37102         }
37103     },
37104
37105         //private
37106     isSelectable : function(rowIndex, colIndex, cm){
37107         return !cm.isHidden(colIndex);
37108     },
37109
37110     /** @ignore */
37111     handleKeyDown : function(e){
37112         //Roo.log('Cell Sel Model handleKeyDown');
37113         if(!e.isNavKeyPress()){
37114             return;
37115         }
37116         var g = this.grid, s = this.selection;
37117         if(!s){
37118             e.stopEvent();
37119             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
37120             if(cell){
37121                 this.select(cell[0], cell[1]);
37122             }
37123             return;
37124         }
37125         var sm = this;
37126         var walk = function(row, col, step){
37127             return g.walkCells(row, col, step, sm.isSelectable,  sm);
37128         };
37129         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
37130         var newCell;
37131
37132       
37133
37134         switch(k){
37135             case e.TAB:
37136                 // handled by onEditorKey
37137                 if (g.isEditor && g.editing) {
37138                     return;
37139                 }
37140                 if(e.shiftKey) {
37141                     newCell = walk(r, c-1, -1);
37142                 } else {
37143                     newCell = walk(r, c+1, 1);
37144                 }
37145                 break;
37146             
37147             case e.DOWN:
37148                newCell = walk(r+1, c, 1);
37149                 break;
37150             
37151             case e.UP:
37152                 newCell = walk(r-1, c, -1);
37153                 break;
37154             
37155             case e.RIGHT:
37156                 newCell = walk(r, c+1, 1);
37157                 break;
37158             
37159             case e.LEFT:
37160                 newCell = walk(r, c-1, -1);
37161                 break;
37162             
37163             case e.ENTER:
37164                 
37165                 if(g.isEditor && !g.editing){
37166                    g.startEditing(r, c);
37167                    e.stopEvent();
37168                    return;
37169                 }
37170                 
37171                 
37172              break;
37173         };
37174         if(newCell){
37175             this.select(newCell[0], newCell[1]);
37176             e.stopEvent();
37177             
37178         }
37179     },
37180
37181     acceptsNav : function(row, col, cm){
37182         return !cm.isHidden(col) && cm.isCellEditable(col, row);
37183     },
37184     /**
37185      * Selects a cell.
37186      * @param {Number} field (not used) - as it's normally used as a listener
37187      * @param {Number} e - event - fake it by using
37188      *
37189      * var e = Roo.EventObjectImpl.prototype;
37190      * e.keyCode = e.TAB
37191      *
37192      * 
37193      */
37194     onEditorKey : function(field, e){
37195         
37196         var k = e.getKey(),
37197             newCell,
37198             g = this.grid,
37199             ed = g.activeEditor,
37200             forward = false;
37201         ///Roo.log('onEditorKey' + k);
37202         
37203         
37204         if (this.enter_is_tab && k == e.ENTER) {
37205             k = e.TAB;
37206         }
37207         
37208         if(k == e.TAB){
37209             if(e.shiftKey){
37210                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37211             }else{
37212                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37213                 forward = true;
37214             }
37215             
37216             e.stopEvent();
37217             
37218         } else if(k == e.ENTER &&  !e.ctrlKey){
37219             ed.completeEdit();
37220             e.stopEvent();
37221             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37222         
37223                 } else if(k == e.ESC){
37224             ed.cancelEdit();
37225         }
37226                 
37227         if (newCell) {
37228             var ecall = { cell : newCell, forward : forward };
37229             this.fireEvent('beforeeditnext', ecall );
37230             newCell = ecall.cell;
37231                         forward = ecall.forward;
37232         }
37233                 
37234         if(newCell){
37235             //Roo.log('next cell after edit');
37236             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
37237         } else if (forward) {
37238             // tabbed past last
37239             this.fireEvent.defer(100, this, ['tabend',this]);
37240         }
37241     }
37242 });/*
37243  * Based on:
37244  * Ext JS Library 1.1.1
37245  * Copyright(c) 2006-2007, Ext JS, LLC.
37246  *
37247  * Originally Released Under LGPL - original licence link has changed is not relivant.
37248  *
37249  * Fork - LGPL
37250  * <script type="text/javascript">
37251  */
37252  
37253 /**
37254  * @class Roo.grid.EditorGrid
37255  * @extends Roo.grid.Grid
37256  * Class for creating and editable grid.
37257  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
37258  * The container MUST have some type of size defined for the grid to fill. The container will be 
37259  * automatically set to position relative if it isn't already.
37260  * @param {Object} dataSource The data model to bind to
37261  * @param {Object} colModel The column model with info about this grid's columns
37262  */
37263 Roo.grid.EditorGrid = function(container, config){
37264     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
37265     this.getGridEl().addClass("xedit-grid");
37266
37267     if(!this.selModel){
37268         this.selModel = new Roo.grid.CellSelectionModel();
37269     }
37270
37271     this.activeEditor = null;
37272
37273         this.addEvents({
37274             /**
37275              * @event beforeedit
37276              * Fires before cell editing is triggered. The edit event object has the following properties <br />
37277              * <ul style="padding:5px;padding-left:16px;">
37278              * <li>grid - This grid</li>
37279              * <li>record - The record being edited</li>
37280              * <li>field - The field name being edited</li>
37281              * <li>value - The value for the field being edited.</li>
37282              * <li>row - The grid row index</li>
37283              * <li>column - The grid column index</li>
37284              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37285              * </ul>
37286              * @param {Object} e An edit event (see above for description)
37287              */
37288             "beforeedit" : true,
37289             /**
37290              * @event afteredit
37291              * Fires after a cell is edited. <br />
37292              * <ul style="padding:5px;padding-left:16px;">
37293              * <li>grid - This grid</li>
37294              * <li>record - The record being edited</li>
37295              * <li>field - The field name being edited</li>
37296              * <li>value - The value being set</li>
37297              * <li>originalValue - The original value for the field, before the edit.</li>
37298              * <li>row - The grid row index</li>
37299              * <li>column - The grid column index</li>
37300              * </ul>
37301              * @param {Object} e An edit event (see above for description)
37302              */
37303             "afteredit" : true,
37304             /**
37305              * @event validateedit
37306              * Fires after a cell is edited, but before the value is set in the record. 
37307          * You can use this to modify the value being set in the field, Return false
37308              * to cancel the change. The edit event object has the following properties <br />
37309              * <ul style="padding:5px;padding-left:16px;">
37310          * <li>editor - This editor</li>
37311              * <li>grid - This grid</li>
37312              * <li>record - The record being edited</li>
37313              * <li>field - The field name being edited</li>
37314              * <li>value - The value being set</li>
37315              * <li>originalValue - The original value for the field, before the edit.</li>
37316              * <li>row - The grid row index</li>
37317              * <li>column - The grid column index</li>
37318              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37319              * </ul>
37320              * @param {Object} e An edit event (see above for description)
37321              */
37322             "validateedit" : true
37323         });
37324     this.on("bodyscroll", this.stopEditing,  this);
37325     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
37326 };
37327
37328 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
37329     /**
37330      * @cfg {Number} clicksToEdit
37331      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
37332      */
37333     clicksToEdit: 2,
37334
37335     // private
37336     isEditor : true,
37337     // private
37338     trackMouseOver: false, // causes very odd FF errors
37339
37340     onCellDblClick : function(g, row, col){
37341         this.startEditing(row, col);
37342     },
37343
37344     onEditComplete : function(ed, value, startValue){
37345         this.editing = false;
37346         this.activeEditor = null;
37347         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
37348         var r = ed.record;
37349         var field = this.colModel.getDataIndex(ed.col);
37350         var e = {
37351             grid: this,
37352             record: r,
37353             field: field,
37354             originalValue: startValue,
37355             value: value,
37356             row: ed.row,
37357             column: ed.col,
37358             cancel:false,
37359             editor: ed
37360         };
37361         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
37362         cell.show();
37363           
37364         if(String(value) !== String(startValue)){
37365             
37366             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
37367                 r.set(field, e.value);
37368                 // if we are dealing with a combo box..
37369                 // then we also set the 'name' colum to be the displayField
37370                 if (ed.field.displayField && ed.field.name) {
37371                     r.set(ed.field.name, ed.field.el.dom.value);
37372                 }
37373                 
37374                 delete e.cancel; //?? why!!!
37375                 this.fireEvent("afteredit", e);
37376             }
37377         } else {
37378             this.fireEvent("afteredit", e); // always fire it!
37379         }
37380         this.view.focusCell(ed.row, ed.col);
37381     },
37382
37383     /**
37384      * Starts editing the specified for the specified row/column
37385      * @param {Number} rowIndex
37386      * @param {Number} colIndex
37387      */
37388     startEditing : function(row, col){
37389         this.stopEditing();
37390         if(this.colModel.isCellEditable(col, row)){
37391             this.view.ensureVisible(row, col, true);
37392           
37393             var r = this.dataSource.getAt(row);
37394             var field = this.colModel.getDataIndex(col);
37395             var cell = Roo.get(this.view.getCell(row,col));
37396             var e = {
37397                 grid: this,
37398                 record: r,
37399                 field: field,
37400                 value: r.data[field],
37401                 row: row,
37402                 column: col,
37403                 cancel:false 
37404             };
37405             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
37406                 this.editing = true;
37407                 var ed = this.colModel.getCellEditor(col, row);
37408                 
37409                 if (!ed) {
37410                     return;
37411                 }
37412                 if(!ed.rendered){
37413                     ed.render(ed.parentEl || document.body);
37414                 }
37415                 ed.field.reset();
37416                
37417                 cell.hide();
37418                 
37419                 (function(){ // complex but required for focus issues in safari, ie and opera
37420                     ed.row = row;
37421                     ed.col = col;
37422                     ed.record = r;
37423                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
37424                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
37425                     this.activeEditor = ed;
37426                     var v = r.data[field];
37427                     ed.startEdit(this.view.getCell(row, col), v);
37428                     // combo's with 'displayField and name set
37429                     if (ed.field.displayField && ed.field.name) {
37430                         ed.field.el.dom.value = r.data[ed.field.name];
37431                     }
37432                     
37433                     
37434                 }).defer(50, this);
37435             }
37436         }
37437     },
37438         
37439     /**
37440      * Stops any active editing
37441      */
37442     stopEditing : function(){
37443         if(this.activeEditor){
37444             this.activeEditor.completeEdit();
37445         }
37446         this.activeEditor = null;
37447     }
37448 });/*
37449  * Based on:
37450  * Ext JS Library 1.1.1
37451  * Copyright(c) 2006-2007, Ext JS, LLC.
37452  *
37453  * Originally Released Under LGPL - original licence link has changed is not relivant.
37454  *
37455  * Fork - LGPL
37456  * <script type="text/javascript">
37457  */
37458
37459 // private - not really -- you end up using it !
37460 // This is a support class used internally by the Grid components
37461
37462 /**
37463  * @class Roo.grid.GridEditor
37464  * @extends Roo.Editor
37465  * Class for creating and editable grid elements.
37466  * @param {Object} config any settings (must include field)
37467  */
37468 Roo.grid.GridEditor = function(field, config){
37469     if (!config && field.field) {
37470         config = field;
37471         field = Roo.factory(config.field, Roo.form);
37472     }
37473     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
37474     field.monitorTab = false;
37475 };
37476
37477 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
37478     
37479     /**
37480      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
37481      */
37482     
37483     alignment: "tl-tl",
37484     autoSize: "width",
37485     hideEl : false,
37486     cls: "x-small-editor x-grid-editor",
37487     shim:false,
37488     shadow:"frame"
37489 });/*
37490  * Based on:
37491  * Ext JS Library 1.1.1
37492  * Copyright(c) 2006-2007, Ext JS, LLC.
37493  *
37494  * Originally Released Under LGPL - original licence link has changed is not relivant.
37495  *
37496  * Fork - LGPL
37497  * <script type="text/javascript">
37498  */
37499   
37500
37501   
37502 Roo.grid.PropertyRecord = Roo.data.Record.create([
37503     {name:'name',type:'string'},  'value'
37504 ]);
37505
37506
37507 Roo.grid.PropertyStore = function(grid, source){
37508     this.grid = grid;
37509     this.store = new Roo.data.Store({
37510         recordType : Roo.grid.PropertyRecord
37511     });
37512     this.store.on('update', this.onUpdate,  this);
37513     if(source){
37514         this.setSource(source);
37515     }
37516     Roo.grid.PropertyStore.superclass.constructor.call(this);
37517 };
37518
37519
37520
37521 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37522     setSource : function(o){
37523         this.source = o;
37524         this.store.removeAll();
37525         var data = [];
37526         for(var k in o){
37527             if(this.isEditableValue(o[k])){
37528                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37529             }
37530         }
37531         this.store.loadRecords({records: data}, {}, true);
37532     },
37533
37534     onUpdate : function(ds, record, type){
37535         if(type == Roo.data.Record.EDIT){
37536             var v = record.data['value'];
37537             var oldValue = record.modified['value'];
37538             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37539                 this.source[record.id] = v;
37540                 record.commit();
37541                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37542             }else{
37543                 record.reject();
37544             }
37545         }
37546     },
37547
37548     getProperty : function(row){
37549        return this.store.getAt(row);
37550     },
37551
37552     isEditableValue: function(val){
37553         if(val && val instanceof Date){
37554             return true;
37555         }else if(typeof val == 'object' || typeof val == 'function'){
37556             return false;
37557         }
37558         return true;
37559     },
37560
37561     setValue : function(prop, value){
37562         this.source[prop] = value;
37563         this.store.getById(prop).set('value', value);
37564     },
37565
37566     getSource : function(){
37567         return this.source;
37568     }
37569 });
37570
37571 Roo.grid.PropertyColumnModel = function(grid, store){
37572     this.grid = grid;
37573     var g = Roo.grid;
37574     g.PropertyColumnModel.superclass.constructor.call(this, [
37575         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37576         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37577     ]);
37578     this.store = store;
37579     this.bselect = Roo.DomHelper.append(document.body, {
37580         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37581             {tag: 'option', value: 'true', html: 'true'},
37582             {tag: 'option', value: 'false', html: 'false'}
37583         ]
37584     });
37585     Roo.id(this.bselect);
37586     var f = Roo.form;
37587     this.editors = {
37588         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37589         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37590         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37591         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37592         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37593     };
37594     this.renderCellDelegate = this.renderCell.createDelegate(this);
37595     this.renderPropDelegate = this.renderProp.createDelegate(this);
37596 };
37597
37598 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37599     
37600     
37601     nameText : 'Name',
37602     valueText : 'Value',
37603     
37604     dateFormat : 'm/j/Y',
37605     
37606     
37607     renderDate : function(dateVal){
37608         return dateVal.dateFormat(this.dateFormat);
37609     },
37610
37611     renderBool : function(bVal){
37612         return bVal ? 'true' : 'false';
37613     },
37614
37615     isCellEditable : function(colIndex, rowIndex){
37616         return colIndex == 1;
37617     },
37618
37619     getRenderer : function(col){
37620         return col == 1 ?
37621             this.renderCellDelegate : this.renderPropDelegate;
37622     },
37623
37624     renderProp : function(v){
37625         return this.getPropertyName(v);
37626     },
37627
37628     renderCell : function(val){
37629         var rv = val;
37630         if(val instanceof Date){
37631             rv = this.renderDate(val);
37632         }else if(typeof val == 'boolean'){
37633             rv = this.renderBool(val);
37634         }
37635         return Roo.util.Format.htmlEncode(rv);
37636     },
37637
37638     getPropertyName : function(name){
37639         var pn = this.grid.propertyNames;
37640         return pn && pn[name] ? pn[name] : name;
37641     },
37642
37643     getCellEditor : function(colIndex, rowIndex){
37644         var p = this.store.getProperty(rowIndex);
37645         var n = p.data['name'], val = p.data['value'];
37646         
37647         if(typeof(this.grid.customEditors[n]) == 'string'){
37648             return this.editors[this.grid.customEditors[n]];
37649         }
37650         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37651             return this.grid.customEditors[n];
37652         }
37653         if(val instanceof Date){
37654             return this.editors['date'];
37655         }else if(typeof val == 'number'){
37656             return this.editors['number'];
37657         }else if(typeof val == 'boolean'){
37658             return this.editors['boolean'];
37659         }else{
37660             return this.editors['string'];
37661         }
37662     }
37663 });
37664
37665 /**
37666  * @class Roo.grid.PropertyGrid
37667  * @extends Roo.grid.EditorGrid
37668  * This class represents the  interface of a component based property grid control.
37669  * <br><br>Usage:<pre><code>
37670  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37671       
37672  });
37673  // set any options
37674  grid.render();
37675  * </code></pre>
37676   
37677  * @constructor
37678  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37679  * The container MUST have some type of size defined for the grid to fill. The container will be
37680  * automatically set to position relative if it isn't already.
37681  * @param {Object} config A config object that sets properties on this grid.
37682  */
37683 Roo.grid.PropertyGrid = function(container, config){
37684     config = config || {};
37685     var store = new Roo.grid.PropertyStore(this);
37686     this.store = store;
37687     var cm = new Roo.grid.PropertyColumnModel(this, store);
37688     store.store.sort('name', 'ASC');
37689     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37690         ds: store.store,
37691         cm: cm,
37692         enableColLock:false,
37693         enableColumnMove:false,
37694         stripeRows:false,
37695         trackMouseOver: false,
37696         clicksToEdit:1
37697     }, config));
37698     this.getGridEl().addClass('x-props-grid');
37699     this.lastEditRow = null;
37700     this.on('columnresize', this.onColumnResize, this);
37701     this.addEvents({
37702          /**
37703              * @event beforepropertychange
37704              * Fires before a property changes (return false to stop?)
37705              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37706              * @param {String} id Record Id
37707              * @param {String} newval New Value
37708          * @param {String} oldval Old Value
37709              */
37710         "beforepropertychange": true,
37711         /**
37712              * @event propertychange
37713              * Fires after a property changes
37714              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37715              * @param {String} id Record Id
37716              * @param {String} newval New Value
37717          * @param {String} oldval Old Value
37718              */
37719         "propertychange": true
37720     });
37721     this.customEditors = this.customEditors || {};
37722 };
37723 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37724     
37725      /**
37726      * @cfg {Object} customEditors map of colnames=> custom editors.
37727      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37728      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37729      * false disables editing of the field.
37730          */
37731     
37732       /**
37733      * @cfg {Object} propertyNames map of property Names to their displayed value
37734          */
37735     
37736     render : function(){
37737         Roo.grid.PropertyGrid.superclass.render.call(this);
37738         this.autoSize.defer(100, this);
37739     },
37740
37741     autoSize : function(){
37742         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37743         if(this.view){
37744             this.view.fitColumns();
37745         }
37746     },
37747
37748     onColumnResize : function(){
37749         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37750         this.autoSize();
37751     },
37752     /**
37753      * Sets the data for the Grid
37754      * accepts a Key => Value object of all the elements avaiable.
37755      * @param {Object} data  to appear in grid.
37756      */
37757     setSource : function(source){
37758         this.store.setSource(source);
37759         //this.autoSize();
37760     },
37761     /**
37762      * Gets all the data from the grid.
37763      * @return {Object} data  data stored in grid
37764      */
37765     getSource : function(){
37766         return this.store.getSource();
37767     }
37768 });/*
37769  * Based on:
37770  * Ext JS Library 1.1.1
37771  * Copyright(c) 2006-2007, Ext JS, LLC.
37772  *
37773  * Originally Released Under LGPL - original licence link has changed is not relivant.
37774  *
37775  * Fork - LGPL
37776  * <script type="text/javascript">
37777  */
37778  
37779 /**
37780  * @class Roo.LoadMask
37781  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37782  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37783  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37784  * element's UpdateManager load indicator and will be destroyed after the initial load.
37785  * @constructor
37786  * Create a new LoadMask
37787  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37788  * @param {Object} config The config object
37789  */
37790 Roo.LoadMask = function(el, config){
37791     this.el = Roo.get(el);
37792     Roo.apply(this, config);
37793     if(this.store){
37794         this.store.on('beforeload', this.onBeforeLoad, this);
37795         this.store.on('load', this.onLoad, this);
37796         this.store.on('loadexception', this.onLoadException, this);
37797         this.removeMask = false;
37798     }else{
37799         var um = this.el.getUpdateManager();
37800         um.showLoadIndicator = false; // disable the default indicator
37801         um.on('beforeupdate', this.onBeforeLoad, this);
37802         um.on('update', this.onLoad, this);
37803         um.on('failure', this.onLoad, this);
37804         this.removeMask = true;
37805     }
37806 };
37807
37808 Roo.LoadMask.prototype = {
37809     /**
37810      * @cfg {Boolean} removeMask
37811      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37812      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37813      */
37814     /**
37815      * @cfg {String} msg
37816      * The text to display in a centered loading message box (defaults to 'Loading...')
37817      */
37818     msg : 'Loading...',
37819     /**
37820      * @cfg {String} msgCls
37821      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37822      */
37823     msgCls : 'x-mask-loading',
37824
37825     /**
37826      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37827      * @type Boolean
37828      */
37829     disabled: false,
37830
37831     /**
37832      * Disables the mask to prevent it from being displayed
37833      */
37834     disable : function(){
37835        this.disabled = true;
37836     },
37837
37838     /**
37839      * Enables the mask so that it can be displayed
37840      */
37841     enable : function(){
37842         this.disabled = false;
37843     },
37844     
37845     onLoadException : function()
37846     {
37847         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37848             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37849         }
37850         this.el.unmask(this.removeMask);
37851     },
37852     // private
37853     onLoad : function()
37854     {
37855         this.el.unmask(this.removeMask);
37856     },
37857
37858     // private
37859     onBeforeLoad : function(){
37860         if(!this.disabled){
37861             this.el.mask(this.msg, this.msgCls);
37862         }
37863     },
37864
37865     // private
37866     destroy : function(){
37867         if(this.store){
37868             this.store.un('beforeload', this.onBeforeLoad, this);
37869             this.store.un('load', this.onLoad, this);
37870             this.store.un('loadexception', this.onLoadException, this);
37871         }else{
37872             var um = this.el.getUpdateManager();
37873             um.un('beforeupdate', this.onBeforeLoad, this);
37874             um.un('update', this.onLoad, this);
37875             um.un('failure', this.onLoad, this);
37876         }
37877     }
37878 };/*
37879  * Based on:
37880  * Ext JS Library 1.1.1
37881  * Copyright(c) 2006-2007, Ext JS, LLC.
37882  *
37883  * Originally Released Under LGPL - original licence link has changed is not relivant.
37884  *
37885  * Fork - LGPL
37886  * <script type="text/javascript">
37887  */
37888
37889
37890 /**
37891  * @class Roo.XTemplate
37892  * @extends Roo.Template
37893  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
37894 <pre><code>
37895 var t = new Roo.XTemplate(
37896         '&lt;select name="{name}"&gt;',
37897                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
37898         '&lt;/select&gt;'
37899 );
37900  
37901 // then append, applying the master template values
37902  </code></pre>
37903  *
37904  * Supported features:
37905  *
37906  *  Tags:
37907
37908 <pre><code>
37909       {a_variable} - output encoded.
37910       {a_variable.format:("Y-m-d")} - call a method on the variable
37911       {a_variable:raw} - unencoded output
37912       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
37913       {a_variable:this.method_on_template(...)} - call a method on the template object.
37914  
37915 </code></pre>
37916  *  The tpl tag:
37917 <pre><code>
37918         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
37919         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
37920         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
37921         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
37922   
37923         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
37924         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
37925 </code></pre>
37926  *      
37927  */
37928 Roo.XTemplate = function()
37929 {
37930     Roo.XTemplate.superclass.constructor.apply(this, arguments);
37931     if (this.html) {
37932         this.compile();
37933     }
37934 };
37935
37936
37937 Roo.extend(Roo.XTemplate, Roo.Template, {
37938
37939     /**
37940      * The various sub templates
37941      */
37942     tpls : false,
37943     /**
37944      *
37945      * basic tag replacing syntax
37946      * WORD:WORD()
37947      *
37948      * // you can fake an object call by doing this
37949      *  x.t:(test,tesT) 
37950      * 
37951      */
37952     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
37953
37954     /**
37955      * compile the template
37956      *
37957      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
37958      *
37959      */
37960     compile: function()
37961     {
37962         var s = this.html;
37963      
37964         s = ['<tpl>', s, '</tpl>'].join('');
37965     
37966         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
37967             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
37968             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
37969             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
37970             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
37971             m,
37972             id     = 0,
37973             tpls   = [];
37974     
37975         while(true == !!(m = s.match(re))){
37976             var forMatch   = m[0].match(nameRe),
37977                 ifMatch   = m[0].match(ifRe),
37978                 execMatch   = m[0].match(execRe),
37979                 namedMatch   = m[0].match(namedRe),
37980                 
37981                 exp  = null, 
37982                 fn   = null,
37983                 exec = null,
37984                 name = forMatch && forMatch[1] ? forMatch[1] : '';
37985                 
37986             if (ifMatch) {
37987                 // if - puts fn into test..
37988                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
37989                 if(exp){
37990                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
37991                 }
37992             }
37993             
37994             if (execMatch) {
37995                 // exec - calls a function... returns empty if true is  returned.
37996                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
37997                 if(exp){
37998                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
37999                 }
38000             }
38001             
38002             
38003             if (name) {
38004                 // for = 
38005                 switch(name){
38006                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38007                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38008                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38009                 }
38010             }
38011             var uid = namedMatch ? namedMatch[1] : id;
38012             
38013             
38014             tpls.push({
38015                 id:     namedMatch ? namedMatch[1] : id,
38016                 target: name,
38017                 exec:   exec,
38018                 test:   fn,
38019                 body:   m[1] || ''
38020             });
38021             if (namedMatch) {
38022                 s = s.replace(m[0], '');
38023             } else { 
38024                 s = s.replace(m[0], '{xtpl'+ id + '}');
38025             }
38026             ++id;
38027         }
38028         this.tpls = [];
38029         for(var i = tpls.length-1; i >= 0; --i){
38030             this.compileTpl(tpls[i]);
38031             this.tpls[tpls[i].id] = tpls[i];
38032         }
38033         this.master = tpls[tpls.length-1];
38034         return this;
38035     },
38036     /**
38037      * same as applyTemplate, except it's done to one of the subTemplates
38038      * when using named templates, you can do:
38039      *
38040      * var str = pl.applySubTemplate('your-name', values);
38041      *
38042      * 
38043      * @param {Number} id of the template
38044      * @param {Object} values to apply to template
38045      * @param {Object} parent (normaly the instance of this object)
38046      */
38047     applySubTemplate : function(id, values, parent)
38048     {
38049         
38050         
38051         var t = this.tpls[id];
38052         
38053         
38054         try { 
38055             if(t.test && !t.test.call(this, values, parent)){
38056                 return '';
38057             }
38058         } catch(e) {
38059             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38060             Roo.log(e.toString());
38061             Roo.log(t.test);
38062             return ''
38063         }
38064         try { 
38065             
38066             if(t.exec && t.exec.call(this, values, parent)){
38067                 return '';
38068             }
38069         } catch(e) {
38070             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38071             Roo.log(e.toString());
38072             Roo.log(t.exec);
38073             return ''
38074         }
38075         try {
38076             var vs = t.target ? t.target.call(this, values, parent) : values;
38077             parent = t.target ? values : parent;
38078             if(t.target && vs instanceof Array){
38079                 var buf = [];
38080                 for(var i = 0, len = vs.length; i < len; i++){
38081                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38082                 }
38083                 return buf.join('');
38084             }
38085             return t.compiled.call(this, vs, parent);
38086         } catch (e) {
38087             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38088             Roo.log(e.toString());
38089             Roo.log(t.compiled);
38090             return '';
38091         }
38092     },
38093
38094     compileTpl : function(tpl)
38095     {
38096         var fm = Roo.util.Format;
38097         var useF = this.disableFormats !== true;
38098         var sep = Roo.isGecko ? "+" : ",";
38099         var undef = function(str) {
38100             Roo.log("Property not found :"  + str);
38101             return '';
38102         };
38103         
38104         var fn = function(m, name, format, args)
38105         {
38106             //Roo.log(arguments);
38107             args = args ? args.replace(/\\'/g,"'") : args;
38108             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38109             if (typeof(format) == 'undefined') {
38110                 format= 'htmlEncode';
38111             }
38112             if (format == 'raw' ) {
38113                 format = false;
38114             }
38115             
38116             if(name.substr(0, 4) == 'xtpl'){
38117                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38118             }
38119             
38120             // build an array of options to determine if value is undefined..
38121             
38122             // basically get 'xxxx.yyyy' then do
38123             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38124             //    (function () { Roo.log("Property not found"); return ''; })() :
38125             //    ......
38126             
38127             var udef_ar = [];
38128             var lookfor = '';
38129             Roo.each(name.split('.'), function(st) {
38130                 lookfor += (lookfor.length ? '.': '') + st;
38131                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38132             });
38133             
38134             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38135             
38136             
38137             if(format && useF){
38138                 
38139                 args = args ? ',' + args : "";
38140                  
38141                 if(format.substr(0, 5) != "this."){
38142                     format = "fm." + format + '(';
38143                 }else{
38144                     format = 'this.call("'+ format.substr(5) + '", ';
38145                     args = ", values";
38146                 }
38147                 
38148                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38149             }
38150              
38151             if (args.length) {
38152                 // called with xxyx.yuu:(test,test)
38153                 // change to ()
38154                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38155             }
38156             // raw.. - :raw modifier..
38157             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38158             
38159         };
38160         var body;
38161         // branched to use + in gecko and [].join() in others
38162         if(Roo.isGecko){
38163             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38164                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38165                     "';};};";
38166         }else{
38167             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38168             body.push(tpl.body.replace(/(\r\n|\n)/g,
38169                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38170             body.push("'].join('');};};");
38171             body = body.join('');
38172         }
38173         
38174         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38175        
38176         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38177         eval(body);
38178         
38179         return this;
38180     },
38181
38182     applyTemplate : function(values){
38183         return this.master.compiled.call(this, values, {});
38184         //var s = this.subs;
38185     },
38186
38187     apply : function(){
38188         return this.applyTemplate.apply(this, arguments);
38189     }
38190
38191  });
38192
38193 Roo.XTemplate.from = function(el){
38194     el = Roo.getDom(el);
38195     return new Roo.XTemplate(el.value || el.innerHTML);
38196 };/*
38197  * Original code for Roojs - LGPL
38198  * <script type="text/javascript">
38199  */
38200  
38201 /**
38202  * @class Roo.XComponent
38203  * A delayed Element creator...
38204  * Or a way to group chunks of interface together.
38205  * 
38206  * Mypart.xyx = new Roo.XComponent({
38207
38208     parent : 'Mypart.xyz', // empty == document.element.!!
38209     order : '001',
38210     name : 'xxxx'
38211     region : 'xxxx'
38212     disabled : function() {} 
38213      
38214     tree : function() { // return an tree of xtype declared components
38215         var MODULE = this;
38216         return 
38217         {
38218             xtype : 'NestedLayoutPanel',
38219             // technicall
38220         }
38221      ]
38222  *})
38223  *
38224  *
38225  * It can be used to build a big heiracy, with parent etc.
38226  * or you can just use this to render a single compoent to a dom element
38227  * MYPART.render(Roo.Element | String(id) | dom_element )
38228  * 
38229  * @extends Roo.util.Observable
38230  * @constructor
38231  * @param cfg {Object} configuration of component
38232  * 
38233  */
38234 Roo.XComponent = function(cfg) {
38235     Roo.apply(this, cfg);
38236     this.addEvents({ 
38237         /**
38238              * @event built
38239              * Fires when this the componnt is built
38240              * @param {Roo.XComponent} c the component
38241              */
38242         'built' : true
38243         
38244     });
38245     this.region = this.region || 'center'; // default..
38246     Roo.XComponent.register(this);
38247     this.modules = false;
38248     this.el = false; // where the layout goes..
38249     
38250     
38251 }
38252 Roo.extend(Roo.XComponent, Roo.util.Observable, {
38253     /**
38254      * @property el
38255      * The created element (with Roo.factory())
38256      * @type {Roo.Layout}
38257      */
38258     el  : false,
38259     
38260     /**
38261      * @property el
38262      * for BC  - use el in new code
38263      * @type {Roo.Layout}
38264      */
38265     panel : false,
38266     
38267     /**
38268      * @property layout
38269      * for BC  - use el in new code
38270      * @type {Roo.Layout}
38271      */
38272     layout : false,
38273     
38274      /**
38275      * @cfg {Function|boolean} disabled
38276      * If this module is disabled by some rule, return true from the funtion
38277      */
38278     disabled : false,
38279     
38280     /**
38281      * @cfg {String} parent 
38282      * Name of parent element which it get xtype added to..
38283      */
38284     parent: false,
38285     
38286     /**
38287      * @cfg {String} order
38288      * Used to set the order in which elements are created (usefull for multiple tabs)
38289      */
38290     
38291     order : false,
38292     /**
38293      * @cfg {String} name
38294      * String to display while loading.
38295      */
38296     name : false,
38297     /**
38298      * @cfg {String} region
38299      * Region to render component to (defaults to center)
38300      */
38301     region : 'center',
38302     
38303     /**
38304      * @cfg {Array} items
38305      * A single item array - the first element is the root of the tree..
38306      * It's done this way to stay compatible with the Xtype system...
38307      */
38308     items : false,
38309     
38310     /**
38311      * @property _tree
38312      * The method that retuns the tree of parts that make up this compoennt 
38313      * @type {function}
38314      */
38315     _tree  : false,
38316     
38317      /**
38318      * render
38319      * render element to dom or tree
38320      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
38321      */
38322     
38323     render : function(el)
38324     {
38325         
38326         el = el || false;
38327         var hp = this.parent ? 1 : 0;
38328         
38329         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
38330             // if parent is a '#.....' string, then let's use that..
38331             var ename = this.parent.substr(1)
38332             this.parent = false;
38333             el = Roo.get(ename);
38334             if (!el) {
38335                 Roo.log("Warning - element can not be found :#" + ename );
38336                 return;
38337             }
38338         }
38339         
38340         
38341         if (!this.parent) {
38342             
38343             el = el ? Roo.get(el) : false;      
38344             
38345             // it's a top level one..
38346             this.parent =  {
38347                 el : new Roo.BorderLayout(el || document.body, {
38348                 
38349                      center: {
38350                          titlebar: false,
38351                          autoScroll:false,
38352                          closeOnTab: true,
38353                          tabPosition: 'top',
38354                           //resizeTabs: true,
38355                          alwaysShowTabs: el && hp? false :  true,
38356                          hideTabs: el || !hp ? true :  false,
38357                          minTabWidth: 140
38358                      }
38359                  })
38360             }
38361         }
38362         
38363                 
38364                 // The 'tree' method is  '_tree now' 
38365             
38366         var tree = this._tree ? this._tree() : this.tree();
38367         tree.region = tree.region || this.region;
38368         this.el = this.parent.el.addxtype(tree);
38369         this.fireEvent('built', this);
38370         
38371         this.panel = this.el;
38372         this.layout = this.panel.layout;
38373                 this.parentLayout = this.parent.layout  || false;  
38374          
38375     }
38376     
38377 });
38378
38379 Roo.apply(Roo.XComponent, {
38380     
38381     /**
38382      * @property  buildCompleted
38383      * True when the builder has completed building the interface.
38384      * @type Boolean
38385      */
38386     buildCompleted : false,
38387      
38388     /**
38389      * @property  topModule
38390      * the upper most module - uses document.element as it's constructor.
38391      * @type Object
38392      */
38393      
38394     topModule  : false,
38395       
38396     /**
38397      * @property  modules
38398      * array of modules to be created by registration system.
38399      * @type {Array} of Roo.XComponent
38400      */
38401     
38402     modules : [],
38403     /**
38404      * @property  elmodules
38405      * array of modules to be created by which use #ID 
38406      * @type {Array} of Roo.XComponent
38407      */
38408      
38409     elmodules : [],
38410
38411     
38412     /**
38413      * Register components to be built later.
38414      *
38415      * This solves the following issues
38416      * - Building is not done on page load, but after an authentication process has occured.
38417      * - Interface elements are registered on page load
38418      * - Parent Interface elements may not be loaded before child, so this handles that..
38419      * 
38420      *
38421      * example:
38422      * 
38423      * MyApp.register({
38424           order : '000001',
38425           module : 'Pman.Tab.projectMgr',
38426           region : 'center',
38427           parent : 'Pman.layout',
38428           disabled : false,  // or use a function..
38429         })
38430      
38431      * * @param {Object} details about module
38432      */
38433     register : function(obj) {
38434                 
38435                 Roo.XComponent.event.fireEvent('register', obj);
38436                 switch(typeof(obj.disabled) ) {
38437                         
38438                         case 'undefined':
38439                                 break;
38440                         
38441                         case 'function':
38442                                 if ( obj.disabled() ) {
38443                                         return;
38444                                 }
38445                                 break;
38446                         default:
38447                                 if (obj.disabled) {
38448                                         return;
38449                                 }
38450                                 break;
38451                 }
38452                 
38453         this.modules.push(obj);
38454          
38455     },
38456     /**
38457      * convert a string to an object..
38458      * eg. 'AAA.BBB' -> finds AAA.BBB
38459
38460      */
38461     
38462     toObject : function(str)
38463     {
38464         if (!str || typeof(str) == 'object') {
38465             return str;
38466         }
38467         if (str.substring(0,1) == '#') {
38468             return str;
38469         }
38470
38471         var ar = str.split('.');
38472         var rt, o;
38473         rt = ar.shift();
38474             /** eval:var:o */
38475         try {
38476             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
38477         } catch (e) {
38478             throw "Module not found : " + str;
38479         }
38480         
38481         if (o === false) {
38482             throw "Module not found : " + str;
38483         }
38484         Roo.each(ar, function(e) {
38485             if (typeof(o[e]) == 'undefined') {
38486                 throw "Module not found : " + str;
38487             }
38488             o = o[e];
38489         });
38490         
38491         return o;
38492         
38493     },
38494     
38495     
38496     /**
38497      * move modules into their correct place in the tree..
38498      * 
38499      */
38500     preBuild : function ()
38501     {
38502         var _t = this;
38503         Roo.each(this.modules , function (obj)
38504         {
38505             var opar = obj.parent;
38506             try { 
38507                 obj.parent = this.toObject(opar);
38508             } catch(e) {
38509                 Roo.log("parent:toObject failed: " + e.toString());
38510                 return;
38511             }
38512             
38513             if (!obj.parent) {
38514                                 Roo.debug && Roo.log("GOT top level module");
38515                                 Roo.debug && Roo.log(obj);
38516                                 obj.modules = new Roo.util.MixedCollection(false, 
38517                     function(o) { return o.order + '' }
38518                 );
38519                 this.topModule = obj;
38520                 return;
38521             }
38522                         // parent is a string (usually a dom element name..)
38523             if (typeof(obj.parent) == 'string') {
38524                 this.elmodules.push(obj);
38525                 return;
38526             }
38527             if (obj.parent.constructor != Roo.XComponent) {
38528                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
38529             }
38530             if (!obj.parent.modules) {
38531                 obj.parent.modules = new Roo.util.MixedCollection(false, 
38532                     function(o) { return o.order + '' }
38533                 );
38534             }
38535             
38536             obj.parent.modules.add(obj);
38537         }, this);
38538     },
38539     
38540      /**
38541      * make a list of modules to build.
38542      * @return {Array} list of modules. 
38543      */ 
38544     
38545     buildOrder : function()
38546     {
38547         var _this = this;
38548         var cmp = function(a,b) {   
38549             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
38550         };
38551         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
38552             throw "No top level modules to build";
38553         }
38554         
38555         // make a flat list in order of modules to build.
38556         var mods = this.topModule ? [ this.topModule ] : [];
38557                 
38558                 // elmodules (is a list of DOM based modules )
38559         Roo.each(this.elmodules,function(e) { mods.push(e) });
38560
38561         
38562         // add modules to their parents..
38563         var addMod = function(m) {
38564                         Roo.debug && Roo.log("build Order: add: " + m.name);
38565             
38566             mods.push(m);
38567             if (m.modules) {
38568                                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
38569                 m.modules.keySort('ASC',  cmp );
38570                                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
38571
38572                 m.modules.each(addMod);
38573             } else {
38574                                 Roo.debug && Roo.log("build Order: no child modules");
38575                         }
38576             // not sure if this is used any more..
38577             if (m.finalize) {
38578                 m.finalize.name = m.name + " (clean up) ";
38579                 mods.push(m.finalize);
38580             }
38581             
38582         }
38583         if (this.topModule) { 
38584             this.topModule.modules.keySort('ASC',  cmp );
38585             this.topModule.modules.each(addMod);
38586         }
38587         return mods;
38588     },
38589     
38590      /**
38591      * Build the registered modules.
38592      * @param {Object} parent element.
38593      * @param {Function} optional method to call after module has been added.
38594      * 
38595      */ 
38596    
38597     build : function() 
38598     {
38599         
38600         this.preBuild();
38601         var mods = this.buildOrder();
38602       
38603         //this.allmods = mods;
38604         //Roo.debug && Roo.log(mods);
38605         //return;
38606         if (!mods.length) { // should not happen
38607             throw "NO modules!!!";
38608         }
38609         
38610         
38611         var msg = "Building Interface...";
38612         // flash it up as modal - so we store the mask!?
38613         Roo.MessageBox.show({ title: 'loading' });
38614         Roo.MessageBox.show({
38615            title: "Please wait...",
38616            msg: msg,
38617            width:450,
38618            progress:true,
38619            closable:false,
38620            modal: false
38621           
38622         });
38623         var total = mods.length;
38624         
38625         var _this = this;
38626         var progressRun = function() {
38627             if (!mods.length) {
38628                 Roo.debug && Roo.log('hide?');
38629                 Roo.MessageBox.hide();
38630                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
38631                 
38632                 // THE END...
38633                 return false;   
38634             }
38635             
38636             var m = mods.shift();
38637             
38638             
38639             Roo.debug && Roo.log(m);
38640             // not sure if this is supported any more.. - modules that are are just function
38641             if (typeof(m) == 'function') { 
38642                 m.call(this);
38643                 return progressRun.defer(10, _this);
38644             } 
38645             
38646             
38647             msg = "Building Interface " + (total  - mods.length) + 
38648                     " of " + total + 
38649                     (m.name ? (' - ' + m.name) : '');
38650                         Roo.debug && Roo.log(msg);
38651             Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
38652             
38653          
38654             // is the module disabled?
38655             var disabled = (typeof(m.disabled) == 'function') ?
38656                 m.disabled.call(m.module.disabled) : m.disabled;    
38657             
38658             
38659             if (disabled) {
38660                 return progressRun(); // we do not update the display!
38661             }
38662             
38663             // now build 
38664             
38665                         
38666                         
38667             m.render();
38668             // it's 10 on top level, and 1 on others??? why...
38669             return progressRun.defer(10, _this);
38670              
38671         }
38672         progressRun.defer(1, _this);
38673      
38674         
38675         
38676     },
38677         
38678         
38679         /**
38680          * Event Object.
38681          *
38682          *
38683          */
38684         event: false, 
38685     /**
38686          * wrapper for event.on - aliased later..  
38687          * Typically use to register a event handler for register:
38688          *
38689          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
38690          *
38691          */
38692     on : false
38693    
38694     
38695     
38696 });
38697
38698 Roo.XComponent.event = new Roo.util.Observable({
38699                 events : { 
38700                         /**
38701                          * @event register
38702                          * Fires when an Component is registered,
38703                          * set the disable property on the Component to stop registration.
38704                          * @param {Roo.XComponent} c the component being registerd.
38705                          * 
38706                          */
38707                         'register' : true,
38708                         /**
38709                          * @event buildcomplete
38710                          * Fires on the top level element when all elements have been built
38711                          * @param {Roo.XComponent} the top level component.
38712                          */
38713                         'buildcomplete' : true
38714                         
38715                 }
38716 });
38717
38718 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
38719  //<script type="text/javascript">
38720
38721
38722 /**
38723  * @class Roo.Login
38724  * @extends Roo.LayoutDialog
38725  * A generic Login Dialog..... - only one needed in theory!?!?
38726  *
38727  * Fires XComponent builder on success...
38728  * 
38729  * Sends 
38730  *    username,password, lang = for login actions.
38731  *    check = 1 for periodic checking that sesion is valid.
38732  *    passwordRequest = email request password
38733  *    logout = 1 = to logout
38734  * 
38735  * Affects: (this id="????" elements)
38736  *   loading  (removed) (used to indicate application is loading)
38737  *   loading-mask (hides) (used to hide application when it's building loading)
38738  *   
38739  * 
38740  * Usage: 
38741  *    
38742  * 
38743  * Myapp.login = Roo.Login({
38744      url: xxxx,
38745    
38746      realm : 'Myapp', 
38747      
38748      
38749      method : 'POST',
38750      
38751      
38752      * 
38753  })
38754  * 
38755  * 
38756  * 
38757  **/
38758  
38759 Roo.Login = function(cfg)
38760 {
38761     this.addEvents({
38762         'refreshed' : true
38763     });
38764     
38765     Roo.apply(this,cfg);
38766     
38767     Roo.onReady(function() {
38768         this.onLoad();
38769     }, this);
38770     // call parent..
38771     
38772    
38773     Roo.Login.superclass.constructor.call(this, this);
38774     //this.addxtype(this.items[0]);
38775     
38776     
38777 }
38778
38779
38780 Roo.extend(Roo.Login, Roo.LayoutDialog, {
38781     
38782     /**
38783      * @cfg {String} method
38784      * Method used to query for login details.
38785      */
38786     
38787     method : 'POST',
38788     /**
38789      * @cfg {String} url
38790      * URL to query login data. - eg. baseURL + '/Login.php'
38791      */
38792     url : '',
38793     
38794     /**
38795      * @property user
38796      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
38797      * @type {Object} 
38798      */
38799     user : false,
38800     /**
38801      * @property checkFails
38802      * Number of times we have attempted to get authentication check, and failed.
38803      * @type {Number} 
38804      */
38805     checkFails : 0,
38806       /**
38807      * @property intervalID
38808      * The window interval that does the constant login checking.
38809      * @type {Number} 
38810      */
38811     intervalID : 0,
38812     
38813     
38814     onLoad : function() // called on page load...
38815     {
38816         // load 
38817          
38818         if (Roo.get('loading')) { // clear any loading indicator..
38819             Roo.get('loading').remove();
38820         }
38821         
38822         //this.switchLang('en'); // set the language to english..
38823        
38824         this.check({
38825             success:  function(response, opts)  {  // check successfull...
38826             
38827                 var res = this.processResponse(response);
38828                 this.checkFails =0;
38829                 if (!res.success) { // error!
38830                     this.checkFails = 5;
38831                     //console.log('call failure');
38832                     return this.failure(response,opts);
38833                 }
38834                 
38835                 if (!res.data.id) { // id=0 == login failure.
38836                     return this.show();
38837                 }
38838                 
38839                               
38840                         //console.log(success);
38841                 this.fillAuth(res.data);   
38842                 this.checkFails =0;
38843                 Roo.XComponent.build();
38844             },
38845             failure : this.show
38846         });
38847         
38848     }, 
38849     
38850     
38851     check: function(cfg) // called every so often to refresh cookie etc..
38852     {
38853         if (cfg.again) { // could be undefined..
38854             this.checkFails++;
38855         } else {
38856             this.checkFails = 0;
38857         }
38858         var _this = this;
38859         if (this.sending) {
38860             if ( this.checkFails > 4) {
38861                 Roo.MessageBox.alert("Error",  
38862                     "Error getting authentication status. - try reloading, or wait a while", function() {
38863                         _this.sending = false;
38864                     }); 
38865                 return;
38866             }
38867             cfg.again = true;
38868             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
38869             return;
38870         }
38871         this.sending = true;
38872         
38873         Roo.Ajax.request({  
38874             url: this.url,
38875             params: {
38876                 getAuthUser: true
38877             },  
38878             method: this.method,
38879             success:  cfg.success || this.success,
38880             failure : cfg.failure || this.failure,
38881             scope : this,
38882             callCfg : cfg
38883               
38884         });  
38885     }, 
38886     
38887     
38888     logout: function()
38889     {
38890         window.onbeforeunload = function() { }; // false does not work for IE..
38891         this.user = false;
38892         var _this = this;
38893         
38894         Roo.Ajax.request({  
38895             url: this.url,
38896             params: {
38897                 logout: 1
38898             },  
38899             method: 'GET',
38900             failure : function() {
38901                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
38902                     document.location = document.location.toString() + '?ts=' + Math.random();
38903                 });
38904                 
38905             },
38906             success : function() {
38907                 _this.user = false;
38908                 this.checkFails =0;
38909                 // fixme..
38910                 document.location = document.location.toString() + '?ts=' + Math.random();
38911             }
38912               
38913               
38914         }); 
38915     },
38916     
38917     processResponse : function (response)
38918     {
38919         var res = '';
38920         try {
38921             res = Roo.decode(response.responseText);
38922             // oops...
38923             if (typeof(res) != 'object') {
38924                 res = { success : false, errorMsg : res, errors : true };
38925             }
38926             if (typeof(res.success) == 'undefined') {
38927                 res.success = false;
38928             }
38929             
38930         } catch(e) {
38931             res = { success : false,  errorMsg : response.responseText, errors : true };
38932         }
38933         return res;
38934     },
38935     
38936     success : function(response, opts)  // check successfull...
38937     {  
38938         this.sending = false;
38939         var res = this.processResponse(response);
38940         if (!res.success) {
38941             return this.failure(response, opts);
38942         }
38943         if (!res.data || !res.data.id) {
38944             return this.failure(response,opts);
38945         }
38946         //console.log(res);
38947         this.fillAuth(res.data);
38948         
38949         this.checkFails =0;
38950         
38951     },
38952     
38953     
38954     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
38955     {
38956         this.authUser = -1;
38957         this.sending = false;
38958         var res = this.processResponse(response);
38959         //console.log(res);
38960         if ( this.checkFails > 2) {
38961         
38962             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
38963                 "Error getting authentication status. - try reloading"); 
38964             return;
38965         }
38966         opts.callCfg.again = true;
38967         this.check.defer(1000, this, [ opts.callCfg ]);
38968         return;  
38969     },
38970     
38971     
38972     
38973     fillAuth: function(au) {
38974         this.startAuthCheck();
38975         this.authUserId = au.id;
38976         this.authUser = au;
38977         this.lastChecked = new Date();
38978         this.fireEvent('refreshed', au);
38979         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
38980         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
38981         au.lang = au.lang || 'en';
38982         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
38983         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
38984         this.switchLang(au.lang );
38985         
38986      
38987         // open system... - -on setyp..
38988         if (this.authUserId  < 0) {
38989             Roo.MessageBox.alert("Warning", 
38990                 "This is an open system - please set up a admin user with a password.");  
38991         }
38992          
38993         //Pman.onload(); // which should do nothing if it's a re-auth result...
38994         
38995              
38996     },
38997     
38998     startAuthCheck : function() // starter for timeout checking..
38999     {
39000         if (this.intervalID) { // timer already in place...
39001             return false;
39002         }
39003         var _this = this;
39004         this.intervalID =  window.setInterval(function() {
39005               _this.check(false);
39006             }, 120000); // every 120 secs = 2mins..
39007         
39008         
39009     },
39010          
39011     
39012     switchLang : function (lang) 
39013     {
39014         _T = typeof(_T) == 'undefined' ? false : _T;
39015           if (!_T || !lang.length) {
39016             return;
39017         }
39018         
39019         if (!_T && lang != 'en') {
39020             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
39021             return;
39022         }
39023         
39024         if (typeof(_T.en) == 'undefined') {
39025             _T.en = {};
39026             Roo.apply(_T.en, _T);
39027         }
39028         
39029         if (typeof(_T[lang]) == 'undefined') {
39030             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
39031             return;
39032         }
39033         
39034         
39035         Roo.apply(_T, _T[lang]);
39036         // just need to set the text values for everything...
39037         var _this = this;
39038         /* this will not work ...
39039         if (this.form) { 
39040             
39041                
39042             function formLabel(name, val) {
39043                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
39044             }
39045             
39046             formLabel('password', "Password"+':');
39047             formLabel('username', "Email Address"+':');
39048             formLabel('lang', "Language"+':');
39049             this.dialog.setTitle("Login");
39050             this.dialog.buttons[0].setText("Forgot Password");
39051             this.dialog.buttons[1].setText("Login");
39052         }
39053         */
39054         
39055         
39056     },
39057     
39058     
39059     title: "Login",
39060     modal: true,
39061     width:  350,
39062     //height: 230,
39063     height: 180,
39064     shadow: true,
39065     minWidth:200,
39066     minHeight:180,
39067     //proxyDrag: true,
39068     closable: false,
39069     draggable: false,
39070     collapsible: false,
39071     resizable: false,
39072     center: {  // needed??
39073         autoScroll:false,
39074         titlebar: false,
39075        // tabPosition: 'top',
39076         hideTabs: true,
39077         closeOnTab: true,
39078         alwaysShowTabs: false
39079     } ,
39080     listeners : {
39081         
39082         show  : function(dlg)
39083         {
39084             //console.log(this);
39085             this.form = this.layout.getRegion('center').activePanel.form;
39086             this.form.dialog = dlg;
39087             this.buttons[0].form = this.form;
39088             this.buttons[0].dialog = dlg;
39089             this.buttons[1].form = this.form;
39090             this.buttons[1].dialog = dlg;
39091            
39092            //this.resizeToLogo.defer(1000,this);
39093             // this is all related to resizing for logos..
39094             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
39095            //// if (!sz) {
39096              //   this.resizeToLogo.defer(1000,this);
39097              //   return;
39098            // }
39099             //var w = Ext.lib.Dom.getViewWidth() - 100;
39100             //var h = Ext.lib.Dom.getViewHeight() - 100;
39101             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
39102             //this.center();
39103             if (this.disabled) {
39104                 this.hide();
39105                 return;
39106             }
39107             
39108             if (this.user.id < 0) { // used for inital setup situations.
39109                 return;
39110             }
39111             
39112             if (this.intervalID) {
39113                 // remove the timer
39114                 window.clearInterval(this.intervalID);
39115                 this.intervalID = false;
39116             }
39117             
39118             
39119             if (Roo.get('loading')) {
39120                 Roo.get('loading').remove();
39121             }
39122             if (Roo.get('loading-mask')) {
39123                 Roo.get('loading-mask').hide();
39124             }
39125             
39126             //incomming._node = tnode;
39127             this.form.reset();
39128             //this.dialog.modal = !modal;
39129             //this.dialog.show();
39130             this.el.unmask(); 
39131             
39132             
39133             this.form.setValues({
39134                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
39135                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
39136             });
39137             
39138             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
39139             if (this.form.findField('username').getValue().length > 0 ){
39140                 this.form.findField('password').focus();
39141             } else {
39142                this.form.findField('username').focus();
39143             }
39144     
39145         }
39146     },
39147     items : [
39148          {
39149        
39150             xtype : 'ContentPanel',
39151             xns : Roo,
39152             region: 'center',
39153             fitToFrame : true,
39154             
39155             items : [
39156     
39157                 {
39158                
39159                     xtype : 'Form',
39160                     xns : Roo.form,
39161                     labelWidth: 100,
39162                     style : 'margin: 10px;',
39163                     
39164                     listeners : {
39165                         actionfailed : function(f, act) {
39166                             // form can return { errors: .... }
39167                                 
39168                             //act.result.errors // invalid form element list...
39169                             //act.result.errorMsg// invalid form element list...
39170                             
39171                             this.dialog.el.unmask();
39172                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
39173                                         "Login failed - communication error - try again.");
39174                                       
39175                         },
39176                         actioncomplete: function(re, act) {
39177                              
39178                             Roo.state.Manager.set(
39179                                 this.dialog.realm + '.username',  
39180                                     this.findField('username').getValue()
39181                             );
39182                             Roo.state.Manager.set(
39183                                 this.dialog.realm + '.lang',  
39184                                 this.findField('lang').getValue() 
39185                             );
39186                             
39187                             this.dialog.fillAuth(act.result.data);
39188                               
39189                             this.dialog.hide();
39190                             
39191                             if (Roo.get('loading-mask')) {
39192                                 Roo.get('loading-mask').show();
39193                             }
39194                             Roo.XComponent.build();
39195                             
39196                              
39197                             
39198                         }
39199                     },
39200                     items : [
39201                         {
39202                             xtype : 'TextField',
39203                             xns : Roo.form,
39204                             fieldLabel: "Email Address",
39205                             name: 'username',
39206                             width:200,
39207                             autoCreate : {tag: "input", type: "text", size: "20"}
39208                         },
39209                         {
39210                             xtype : 'TextField',
39211                             xns : Roo.form,
39212                             fieldLabel: "Password",
39213                             inputType: 'password',
39214                             name: 'password',
39215                             width:200,
39216                             autoCreate : {tag: "input", type: "text", size: "20"},
39217                             listeners : {
39218                                 specialkey : function(e,ev) {
39219                                     if (ev.keyCode == 13) {
39220                                         this.form.dialog.el.mask("Logging in");
39221                                         this.form.doAction('submit', {
39222                                             url: this.form.dialog.url,
39223                                             method: this.form.dialog.method
39224                                         });
39225                                     }
39226                                 }
39227                             }  
39228                         },
39229                         {
39230                             xtype : 'ComboBox',
39231                             xns : Roo.form,
39232                             fieldLabel: "Language",
39233                             name : 'langdisp',
39234                             store: {
39235                                 xtype : 'SimpleStore',
39236                                 fields: ['lang', 'ldisp'],
39237                                 data : [
39238                                     [ 'en', 'English' ],
39239                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
39240                                     [ 'zh_CN', '\u7C21\u4E2D' ]
39241                                 ]
39242                             },
39243                             
39244                             valueField : 'lang',
39245                             hiddenName:  'lang',
39246                             width: 200,
39247                             displayField:'ldisp',
39248                             typeAhead: false,
39249                             editable: false,
39250                             mode: 'local',
39251                             triggerAction: 'all',
39252                             emptyText:'Select a Language...',
39253                             selectOnFocus:true,
39254                             listeners : {
39255                                 select :  function(cb, rec, ix) {
39256                                     this.form.switchLang(rec.data.lang);
39257                                 }
39258                             }
39259                         
39260                         }
39261                     ]
39262                 }
39263                   
39264                 
39265             ]
39266         }
39267     ],
39268     buttons : [
39269         {
39270             xtype : 'Button',
39271             xns : 'Roo',
39272             text : "Forgot Password",
39273             listeners : {
39274                 click : function() {
39275                     //console.log(this);
39276                     var n = this.form.findField('username').getValue();
39277                     if (!n.length) {
39278                         Roo.MessageBox.alert("Error", "Fill in your email address");
39279                         return;
39280                     }
39281                     Roo.Ajax.request({
39282                         url: this.dialog.url,
39283                         params: {
39284                             passwordRequest: n
39285                         },
39286                         method: this.dialog.method,
39287                         success:  function(response, opts)  {  // check successfull...
39288                         
39289                             var res = this.dialog.processResponse(response);
39290                             if (!res.success) { // error!
39291                                Roo.MessageBox.alert("Error" ,
39292                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
39293                                return;
39294                             }
39295                             Roo.MessageBox.alert("Notice" ,
39296                                 "Please check you email for the Password Reset message");
39297                         },
39298                         failure : function() {
39299                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
39300                         }
39301                         
39302                     });
39303                 }
39304             }
39305         },
39306         {
39307             xtype : 'Button',
39308             xns : 'Roo',
39309             text : "Login",
39310             listeners : {
39311                 
39312                 click : function () {
39313                         
39314                     this.dialog.el.mask("Logging in");
39315                     this.form.doAction('submit', {
39316                             url: this.dialog.url,
39317                             method: this.dialog.method
39318                     });
39319                 }
39320             }
39321         }
39322     ]
39323   
39324   
39325 })
39326  
39327
39328
39329