Merge branch 'master' of https://github.com/roojs/roojs1 into github
[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){
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     };
4725
4726     if(config && config.data){
4727         this.inlineData = config.data;
4728         delete config.data;
4729     }
4730
4731     Roo.apply(this, config);
4732     
4733     if(this.reader){ // reader passed
4734         this.reader = Roo.factory(this.reader, Roo.data);
4735         this.reader.xmodule = this.xmodule || false;
4736         if(!this.recordType){
4737             this.recordType = this.reader.recordType;
4738         }
4739         if(this.reader.onMetaChange){
4740             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4741         }
4742     }
4743
4744     if(this.recordType){
4745         this.fields = this.recordType.prototype.fields;
4746     }
4747     this.modified = [];
4748
4749     this.addEvents({
4750         /**
4751          * @event datachanged
4752          * Fires when the data cache has changed, and a widget which is using this Store
4753          * as a Record cache should refresh its view.
4754          * @param {Store} this
4755          */
4756         datachanged : true,
4757         /**
4758          * @event metachange
4759          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4760          * @param {Store} this
4761          * @param {Object} meta The JSON metadata
4762          */
4763         metachange : true,
4764         /**
4765          * @event add
4766          * Fires when Records have been added to the Store
4767          * @param {Store} this
4768          * @param {Roo.data.Record[]} records The array of Records added
4769          * @param {Number} index The index at which the record(s) were added
4770          */
4771         add : true,
4772         /**
4773          * @event remove
4774          * Fires when a Record has been removed from the Store
4775          * @param {Store} this
4776          * @param {Roo.data.Record} record The Record that was removed
4777          * @param {Number} index The index at which the record was removed
4778          */
4779         remove : true,
4780         /**
4781          * @event update
4782          * Fires when a Record has been updated
4783          * @param {Store} this
4784          * @param {Roo.data.Record} record The Record that was updated
4785          * @param {String} operation The update operation being performed.  Value may be one of:
4786          * <pre><code>
4787  Roo.data.Record.EDIT
4788  Roo.data.Record.REJECT
4789  Roo.data.Record.COMMIT
4790          * </code></pre>
4791          */
4792         update : true,
4793         /**
4794          * @event clear
4795          * Fires when the data cache has been cleared.
4796          * @param {Store} this
4797          */
4798         clear : true,
4799         /**
4800          * @event beforeload
4801          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4802          * the load action will be canceled.
4803          * @param {Store} this
4804          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4805          */
4806         beforeload : true,
4807         /**
4808          * @event load
4809          * Fires after a new set of Records has been loaded.
4810          * @param {Store} this
4811          * @param {Roo.data.Record[]} records The Records that were loaded
4812          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4813          */
4814         load : true,
4815         /**
4816          * @event loadexception
4817          * Fires if an exception occurs in the Proxy during loading.
4818          * Called with the signature of the Proxy's "loadexception" event.
4819          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4820          * 
4821          * @param {Proxy} 
4822          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4823          * @param {Object} load options 
4824          * @param {Object} jsonData from your request (normally this contains the Exception)
4825          */
4826         loadexception : true
4827     });
4828     
4829     if(this.proxy){
4830         this.proxy = Roo.factory(this.proxy, Roo.data);
4831         this.proxy.xmodule = this.xmodule || false;
4832         this.relayEvents(this.proxy,  ["loadexception"]);
4833     }
4834     this.sortToggle = {};
4835
4836     Roo.data.Store.superclass.constructor.call(this);
4837
4838     if(this.inlineData){
4839         this.loadData(this.inlineData);
4840         delete this.inlineData;
4841     }
4842 };
4843 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4844      /**
4845     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4846     * without a remote query - used by combo/forms at present.
4847     */
4848     
4849     /**
4850     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4851     */
4852     /**
4853     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4854     */
4855     /**
4856     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4857     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4858     */
4859     /**
4860     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4861     * on any HTTP request
4862     */
4863     /**
4864     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4865     */
4866     /**
4867     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4868     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4869     */
4870     remoteSort : false,
4871
4872     /**
4873     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4874      * loaded or when a record is removed. (defaults to false).
4875     */
4876     pruneModifiedRecords : false,
4877
4878     // private
4879     lastOptions : null,
4880
4881     /**
4882      * Add Records to the Store and fires the add event.
4883      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4884      */
4885     add : function(records){
4886         records = [].concat(records);
4887         for(var i = 0, len = records.length; i < len; i++){
4888             records[i].join(this);
4889         }
4890         var index = this.data.length;
4891         this.data.addAll(records);
4892         this.fireEvent("add", this, records, index);
4893     },
4894
4895     /**
4896      * Remove a Record from the Store and fires the remove event.
4897      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4898      */
4899     remove : function(record){
4900         var index = this.data.indexOf(record);
4901         this.data.removeAt(index);
4902         if(this.pruneModifiedRecords){
4903             this.modified.remove(record);
4904         }
4905         this.fireEvent("remove", this, record, index);
4906     },
4907
4908     /**
4909      * Remove all Records from the Store and fires the clear event.
4910      */
4911     removeAll : function(){
4912         this.data.clear();
4913         if(this.pruneModifiedRecords){
4914             this.modified = [];
4915         }
4916         this.fireEvent("clear", this);
4917     },
4918
4919     /**
4920      * Inserts Records to the Store at the given index and fires the add event.
4921      * @param {Number} index The start index at which to insert the passed Records.
4922      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4923      */
4924     insert : function(index, records){
4925         records = [].concat(records);
4926         for(var i = 0, len = records.length; i < len; i++){
4927             this.data.insert(index, records[i]);
4928             records[i].join(this);
4929         }
4930         this.fireEvent("add", this, records, index);
4931     },
4932
4933     /**
4934      * Get the index within the cache of the passed Record.
4935      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4936      * @return {Number} The index of the passed Record. Returns -1 if not found.
4937      */
4938     indexOf : function(record){
4939         return this.data.indexOf(record);
4940     },
4941
4942     /**
4943      * Get the index within the cache of the Record with the passed id.
4944      * @param {String} id The id of the Record to find.
4945      * @return {Number} The index of the Record. Returns -1 if not found.
4946      */
4947     indexOfId : function(id){
4948         return this.data.indexOfKey(id);
4949     },
4950
4951     /**
4952      * Get the Record with the specified id.
4953      * @param {String} id The id of the Record to find.
4954      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4955      */
4956     getById : function(id){
4957         return this.data.key(id);
4958     },
4959
4960     /**
4961      * Get the Record at the specified index.
4962      * @param {Number} index The index of the Record to find.
4963      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4964      */
4965     getAt : function(index){
4966         return this.data.itemAt(index);
4967     },
4968
4969     /**
4970      * Returns a range of Records between specified indices.
4971      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4972      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4973      * @return {Roo.data.Record[]} An array of Records
4974      */
4975     getRange : function(start, end){
4976         return this.data.getRange(start, end);
4977     },
4978
4979     // private
4980     storeOptions : function(o){
4981         o = Roo.apply({}, o);
4982         delete o.callback;
4983         delete o.scope;
4984         this.lastOptions = o;
4985     },
4986
4987     /**
4988      * Loads the Record cache from the configured Proxy using the configured Reader.
4989      * <p>
4990      * If using remote paging, then the first load call must specify the <em>start</em>
4991      * and <em>limit</em> properties in the options.params property to establish the initial
4992      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4993      * <p>
4994      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4995      * and this call will return before the new data has been loaded. Perform any post-processing
4996      * in a callback function, or in a "load" event handler.</strong>
4997      * <p>
4998      * @param {Object} options An object containing properties which control loading options:<ul>
4999      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5000      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5001      * passed the following arguments:<ul>
5002      * <li>r : Roo.data.Record[]</li>
5003      * <li>options: Options object from the load call</li>
5004      * <li>success: Boolean success indicator</li></ul></li>
5005      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5006      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5007      * </ul>
5008      */
5009     load : function(options){
5010         options = options || {};
5011         if(this.fireEvent("beforeload", this, options) !== false){
5012             this.storeOptions(options);
5013             var p = Roo.apply(options.params || {}, this.baseParams);
5014             // if meta was not loaded from remote source.. try requesting it.
5015             if (!this.reader.metaFromRemote) {
5016                 p._requestMeta = 1;
5017             }
5018             if(this.sortInfo && this.remoteSort){
5019                 var pn = this.paramNames;
5020                 p[pn["sort"]] = this.sortInfo.field;
5021                 p[pn["dir"]] = this.sortInfo.direction;
5022             }
5023             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5024         }
5025     },
5026
5027     /**
5028      * Reloads the Record cache from the configured Proxy using the configured Reader and
5029      * the options from the last load operation performed.
5030      * @param {Object} options (optional) An object containing properties which may override the options
5031      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5032      * the most recently used options are reused).
5033      */
5034     reload : function(options){
5035         this.load(Roo.applyIf(options||{}, this.lastOptions));
5036     },
5037
5038     // private
5039     // Called as a callback by the Reader during a load operation.
5040     loadRecords : function(o, options, success){
5041         if(!o || success === false){
5042             if(success !== false){
5043                 this.fireEvent("load", this, [], options);
5044             }
5045             if(options.callback){
5046                 options.callback.call(options.scope || this, [], options, false);
5047             }
5048             return;
5049         }
5050         // if data returned failure - throw an exception.
5051         if (o.success === false) {
5052             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5053             return;
5054         }
5055         var r = o.records, t = o.totalRecords || r.length;
5056         if(!options || options.add !== true){
5057             if(this.pruneModifiedRecords){
5058                 this.modified = [];
5059             }
5060             for(var i = 0, len = r.length; i < len; i++){
5061                 r[i].join(this);
5062             }
5063             if(this.snapshot){
5064                 this.data = this.snapshot;
5065                 delete this.snapshot;
5066             }
5067             this.data.clear();
5068             this.data.addAll(r);
5069             this.totalLength = t;
5070             this.applySort();
5071             this.fireEvent("datachanged", this);
5072         }else{
5073             this.totalLength = Math.max(t, this.data.length+r.length);
5074             this.add(r);
5075         }
5076         this.fireEvent("load", this, r, options);
5077         if(options.callback){
5078             options.callback.call(options.scope || this, r, options, true);
5079         }
5080     },
5081
5082     /**
5083      * Loads data from a passed data block. A Reader which understands the format of the data
5084      * must have been configured in the constructor.
5085      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5086      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5087      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5088      */
5089     loadData : function(o, append){
5090         var r = this.reader.readRecords(o);
5091         this.loadRecords(r, {add: append}, true);
5092     },
5093
5094     /**
5095      * Gets the number of cached records.
5096      * <p>
5097      * <em>If using paging, this may not be the total size of the dataset. If the data object
5098      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5099      * the data set size</em>
5100      */
5101     getCount : function(){
5102         return this.data.length || 0;
5103     },
5104
5105     /**
5106      * Gets the total number of records in the dataset as returned by the server.
5107      * <p>
5108      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5109      * the dataset size</em>
5110      */
5111     getTotalCount : function(){
5112         return this.totalLength || 0;
5113     },
5114
5115     /**
5116      * Returns the sort state of the Store as an object with two properties:
5117      * <pre><code>
5118  field {String} The name of the field by which the Records are sorted
5119  direction {String} The sort order, "ASC" or "DESC"
5120      * </code></pre>
5121      */
5122     getSortState : function(){
5123         return this.sortInfo;
5124     },
5125
5126     // private
5127     applySort : function(){
5128         if(this.sortInfo && !this.remoteSort){
5129             var s = this.sortInfo, f = s.field;
5130             var st = this.fields.get(f).sortType;
5131             var fn = function(r1, r2){
5132                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5133                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5134             };
5135             this.data.sort(s.direction, fn);
5136             if(this.snapshot && this.snapshot != this.data){
5137                 this.snapshot.sort(s.direction, fn);
5138             }
5139         }
5140     },
5141
5142     /**
5143      * Sets the default sort column and order to be used by the next load operation.
5144      * @param {String} fieldName The name of the field to sort by.
5145      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5146      */
5147     setDefaultSort : function(field, dir){
5148         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5149     },
5150
5151     /**
5152      * Sort the Records.
5153      * If remote sorting is used, the sort is performed on the server, and the cache is
5154      * reloaded. If local sorting is used, the cache is sorted internally.
5155      * @param {String} fieldName The name of the field to sort by.
5156      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5157      */
5158     sort : function(fieldName, dir){
5159         var f = this.fields.get(fieldName);
5160         if(!dir){
5161             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5162                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5163             }else{
5164                 dir = f.sortDir;
5165             }
5166         }
5167         this.sortToggle[f.name] = dir;
5168         this.sortInfo = {field: f.name, direction: dir};
5169         if(!this.remoteSort){
5170             this.applySort();
5171             this.fireEvent("datachanged", this);
5172         }else{
5173             this.load(this.lastOptions);
5174         }
5175     },
5176
5177     /**
5178      * Calls the specified function for each of the Records in the cache.
5179      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5180      * Returning <em>false</em> aborts and exits the iteration.
5181      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5182      */
5183     each : function(fn, scope){
5184         this.data.each(fn, scope);
5185     },
5186
5187     /**
5188      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5189      * (e.g., during paging).
5190      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5191      */
5192     getModifiedRecords : function(){
5193         return this.modified;
5194     },
5195
5196     // private
5197     createFilterFn : function(property, value, anyMatch){
5198         if(!value.exec){ // not a regex
5199             value = String(value);
5200             if(value.length == 0){
5201                 return false;
5202             }
5203             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5204         }
5205         return function(r){
5206             return value.test(r.data[property]);
5207         };
5208     },
5209
5210     /**
5211      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5212      * @param {String} property A field on your records
5213      * @param {Number} start The record index to start at (defaults to 0)
5214      * @param {Number} end The last record index to include (defaults to length - 1)
5215      * @return {Number} The sum
5216      */
5217     sum : function(property, start, end){
5218         var rs = this.data.items, v = 0;
5219         start = start || 0;
5220         end = (end || end === 0) ? end : rs.length-1;
5221
5222         for(var i = start; i <= end; i++){
5223             v += (rs[i].data[property] || 0);
5224         }
5225         return v;
5226     },
5227
5228     /**
5229      * Filter the records by a specified property.
5230      * @param {String} field A field on your records
5231      * @param {String/RegExp} value Either a string that the field
5232      * should start with or a RegExp to test against the field
5233      * @param {Boolean} anyMatch True to match any part not just the beginning
5234      */
5235     filter : function(property, value, anyMatch){
5236         var fn = this.createFilterFn(property, value, anyMatch);
5237         return fn ? this.filterBy(fn) : this.clearFilter();
5238     },
5239
5240     /**
5241      * Filter by a function. The specified function will be called with each
5242      * record in this data source. If the function returns true the record is included,
5243      * otherwise it is filtered.
5244      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5245      * @param {Object} scope (optional) The scope of the function (defaults to this)
5246      */
5247     filterBy : function(fn, scope){
5248         this.snapshot = this.snapshot || this.data;
5249         this.data = this.queryBy(fn, scope||this);
5250         this.fireEvent("datachanged", this);
5251     },
5252
5253     /**
5254      * Query the records by a specified property.
5255      * @param {String} field A field on your records
5256      * @param {String/RegExp} value Either a string that the field
5257      * should start with or a RegExp to test against the field
5258      * @param {Boolean} anyMatch True to match any part not just the beginning
5259      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5260      */
5261     query : function(property, value, anyMatch){
5262         var fn = this.createFilterFn(property, value, anyMatch);
5263         return fn ? this.queryBy(fn) : this.data.clone();
5264     },
5265
5266     /**
5267      * Query by a function. The specified function will be called with each
5268      * record in this data source. If the function returns true the record is included
5269      * in the results.
5270      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5271      * @param {Object} scope (optional) The scope of the function (defaults to this)
5272       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5273      **/
5274     queryBy : function(fn, scope){
5275         var data = this.snapshot || this.data;
5276         return data.filterBy(fn, scope||this);
5277     },
5278
5279     /**
5280      * Collects unique values for a particular dataIndex from this store.
5281      * @param {String} dataIndex The property to collect
5282      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5283      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5284      * @return {Array} An array of the unique values
5285      **/
5286     collect : function(dataIndex, allowNull, bypassFilter){
5287         var d = (bypassFilter === true && this.snapshot) ?
5288                 this.snapshot.items : this.data.items;
5289         var v, sv, r = [], l = {};
5290         for(var i = 0, len = d.length; i < len; i++){
5291             v = d[i].data[dataIndex];
5292             sv = String(v);
5293             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5294                 l[sv] = true;
5295                 r[r.length] = v;
5296             }
5297         }
5298         return r;
5299     },
5300
5301     /**
5302      * Revert to a view of the Record cache with no filtering applied.
5303      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5304      */
5305     clearFilter : function(suppressEvent){
5306         if(this.snapshot && this.snapshot != this.data){
5307             this.data = this.snapshot;
5308             delete this.snapshot;
5309             if(suppressEvent !== true){
5310                 this.fireEvent("datachanged", this);
5311             }
5312         }
5313     },
5314
5315     // private
5316     afterEdit : function(record){
5317         if(this.modified.indexOf(record) == -1){
5318             this.modified.push(record);
5319         }
5320         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5321     },
5322
5323     // private
5324     afterReject : function(record){
5325         this.modified.remove(record);
5326         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5327     },
5328
5329     // private
5330     afterCommit : function(record){
5331         this.modified.remove(record);
5332         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5333     },
5334
5335     /**
5336      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5337      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5338      */
5339     commitChanges : function(){
5340         var m = this.modified.slice(0);
5341         this.modified = [];
5342         for(var i = 0, len = m.length; i < len; i++){
5343             m[i].commit();
5344         }
5345     },
5346
5347     /**
5348      * Cancel outstanding changes on all changed records.
5349      */
5350     rejectChanges : function(){
5351         var m = this.modified.slice(0);
5352         this.modified = [];
5353         for(var i = 0, len = m.length; i < len; i++){
5354             m[i].reject();
5355         }
5356     },
5357
5358     onMetaChange : function(meta, rtype, o){
5359         this.recordType = rtype;
5360         this.fields = rtype.prototype.fields;
5361         delete this.snapshot;
5362         this.sortInfo = meta.sortInfo || this.sortInfo;
5363         this.modified = [];
5364         this.fireEvent('metachange', this, this.reader.meta);
5365     }
5366 });/*
5367  * Based on:
5368  * Ext JS Library 1.1.1
5369  * Copyright(c) 2006-2007, Ext JS, LLC.
5370  *
5371  * Originally Released Under LGPL - original licence link has changed is not relivant.
5372  *
5373  * Fork - LGPL
5374  * <script type="text/javascript">
5375  */
5376
5377 /**
5378  * @class Roo.data.SimpleStore
5379  * @extends Roo.data.Store
5380  * Small helper class to make creating Stores from Array data easier.
5381  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5382  * @cfg {Array} fields An array of field definition objects, or field name strings.
5383  * @cfg {Array} data The multi-dimensional array of data
5384  * @constructor
5385  * @param {Object} config
5386  */
5387 Roo.data.SimpleStore = function(config){
5388     Roo.data.SimpleStore.superclass.constructor.call(this, {
5389         isLocal : true,
5390         reader: new Roo.data.ArrayReader({
5391                 id: config.id
5392             },
5393             Roo.data.Record.create(config.fields)
5394         ),
5395         proxy : new Roo.data.MemoryProxy(config.data)
5396     });
5397     this.load();
5398 };
5399 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5400  * Based on:
5401  * Ext JS Library 1.1.1
5402  * Copyright(c) 2006-2007, Ext JS, LLC.
5403  *
5404  * Originally Released Under LGPL - original licence link has changed is not relivant.
5405  *
5406  * Fork - LGPL
5407  * <script type="text/javascript">
5408  */
5409
5410 /**
5411 /**
5412  * @extends Roo.data.Store
5413  * @class Roo.data.JsonStore
5414  * Small helper class to make creating Stores for JSON data easier. <br/>
5415 <pre><code>
5416 var store = new Roo.data.JsonStore({
5417     url: 'get-images.php',
5418     root: 'images',
5419     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5420 });
5421 </code></pre>
5422  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5423  * JsonReader and HttpProxy (unless inline data is provided).</b>
5424  * @cfg {Array} fields An array of field definition objects, or field name strings.
5425  * @constructor
5426  * @param {Object} config
5427  */
5428 Roo.data.JsonStore = function(c){
5429     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5430         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5431         reader: new Roo.data.JsonReader(c, c.fields)
5432     }));
5433 };
5434 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5435  * Based on:
5436  * Ext JS Library 1.1.1
5437  * Copyright(c) 2006-2007, Ext JS, LLC.
5438  *
5439  * Originally Released Under LGPL - original licence link has changed is not relivant.
5440  *
5441  * Fork - LGPL
5442  * <script type="text/javascript">
5443  */
5444
5445  
5446 Roo.data.Field = function(config){
5447     if(typeof config == "string"){
5448         config = {name: config};
5449     }
5450     Roo.apply(this, config);
5451     
5452     if(!this.type){
5453         this.type = "auto";
5454     }
5455     
5456     var st = Roo.data.SortTypes;
5457     // named sortTypes are supported, here we look them up
5458     if(typeof this.sortType == "string"){
5459         this.sortType = st[this.sortType];
5460     }
5461     
5462     // set default sortType for strings and dates
5463     if(!this.sortType){
5464         switch(this.type){
5465             case "string":
5466                 this.sortType = st.asUCString;
5467                 break;
5468             case "date":
5469                 this.sortType = st.asDate;
5470                 break;
5471             default:
5472                 this.sortType = st.none;
5473         }
5474     }
5475
5476     // define once
5477     var stripRe = /[\$,%]/g;
5478
5479     // prebuilt conversion function for this field, instead of
5480     // switching every time we're reading a value
5481     if(!this.convert){
5482         var cv, dateFormat = this.dateFormat;
5483         switch(this.type){
5484             case "":
5485             case "auto":
5486             case undefined:
5487                 cv = function(v){ return v; };
5488                 break;
5489             case "string":
5490                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5491                 break;
5492             case "int":
5493                 cv = function(v){
5494                     return v !== undefined && v !== null && v !== '' ?
5495                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5496                     };
5497                 break;
5498             case "float":
5499                 cv = function(v){
5500                     return v !== undefined && v !== null && v !== '' ?
5501                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5502                     };
5503                 break;
5504             case "bool":
5505             case "boolean":
5506                 cv = function(v){ return v === true || v === "true" || v == 1; };
5507                 break;
5508             case "date":
5509                 cv = function(v){
5510                     if(!v){
5511                         return '';
5512                     }
5513                     if(v instanceof Date){
5514                         return v;
5515                     }
5516                     if(dateFormat){
5517                         if(dateFormat == "timestamp"){
5518                             return new Date(v*1000);
5519                         }
5520                         return Date.parseDate(v, dateFormat);
5521                     }
5522                     var parsed = Date.parse(v);
5523                     return parsed ? new Date(parsed) : null;
5524                 };
5525              break;
5526             
5527         }
5528         this.convert = cv;
5529     }
5530 };
5531
5532 Roo.data.Field.prototype = {
5533     dateFormat: null,
5534     defaultValue: "",
5535     mapping: null,
5536     sortType : null,
5537     sortDir : "ASC"
5538 };/*
5539  * Based on:
5540  * Ext JS Library 1.1.1
5541  * Copyright(c) 2006-2007, Ext JS, LLC.
5542  *
5543  * Originally Released Under LGPL - original licence link has changed is not relivant.
5544  *
5545  * Fork - LGPL
5546  * <script type="text/javascript">
5547  */
5548  
5549 // Base class for reading structured data from a data source.  This class is intended to be
5550 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5551
5552 /**
5553  * @class Roo.data.DataReader
5554  * Base class for reading structured data from a data source.  This class is intended to be
5555  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5556  */
5557
5558 Roo.data.DataReader = function(meta, recordType){
5559     
5560     this.meta = meta;
5561     
5562     this.recordType = recordType instanceof Array ? 
5563         Roo.data.Record.create(recordType) : recordType;
5564 };
5565
5566 Roo.data.DataReader.prototype = {
5567      /**
5568      * Create an empty record
5569      * @param {Object} data (optional) - overlay some values
5570      * @return {Roo.data.Record} record created.
5571      */
5572     newRow :  function(d) {
5573         var da =  {};
5574         this.recordType.prototype.fields.each(function(c) {
5575             switch( c.type) {
5576                 case 'int' : da[c.name] = 0; break;
5577                 case 'date' : da[c.name] = new Date(); break;
5578                 case 'float' : da[c.name] = 0.0; break;
5579                 case 'boolean' : da[c.name] = false; break;
5580                 default : da[c.name] = ""; break;
5581             }
5582             
5583         });
5584         return new this.recordType(Roo.apply(da, d));
5585     }
5586     
5587 };/*
5588  * Based on:
5589  * Ext JS Library 1.1.1
5590  * Copyright(c) 2006-2007, Ext JS, LLC.
5591  *
5592  * Originally Released Under LGPL - original licence link has changed is not relivant.
5593  *
5594  * Fork - LGPL
5595  * <script type="text/javascript">
5596  */
5597
5598 /**
5599  * @class Roo.data.DataProxy
5600  * @extends Roo.data.Observable
5601  * This class is an abstract base class for implementations which provide retrieval of
5602  * unformatted data objects.<br>
5603  * <p>
5604  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5605  * (of the appropriate type which knows how to parse the data object) to provide a block of
5606  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5607  * <p>
5608  * Custom implementations must implement the load method as described in
5609  * {@link Roo.data.HttpProxy#load}.
5610  */
5611 Roo.data.DataProxy = function(){
5612     this.addEvents({
5613         /**
5614          * @event beforeload
5615          * Fires before a network request is made to retrieve a data object.
5616          * @param {Object} This DataProxy object.
5617          * @param {Object} params The params parameter to the load function.
5618          */
5619         beforeload : true,
5620         /**
5621          * @event load
5622          * Fires before the load method's callback is called.
5623          * @param {Object} This DataProxy object.
5624          * @param {Object} o The data object.
5625          * @param {Object} arg The callback argument object passed to the load function.
5626          */
5627         load : true,
5628         /**
5629          * @event loadexception
5630          * Fires if an Exception occurs during data retrieval.
5631          * @param {Object} This DataProxy object.
5632          * @param {Object} o The data object.
5633          * @param {Object} arg The callback argument object passed to the load function.
5634          * @param {Object} e The Exception.
5635          */
5636         loadexception : true
5637     });
5638     Roo.data.DataProxy.superclass.constructor.call(this);
5639 };
5640
5641 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5642
5643     /**
5644      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5645      */
5646 /*
5647  * Based on:
5648  * Ext JS Library 1.1.1
5649  * Copyright(c) 2006-2007, Ext JS, LLC.
5650  *
5651  * Originally Released Under LGPL - original licence link has changed is not relivant.
5652  *
5653  * Fork - LGPL
5654  * <script type="text/javascript">
5655  */
5656 /**
5657  * @class Roo.data.MemoryProxy
5658  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5659  * to the Reader when its load method is called.
5660  * @constructor
5661  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5662  */
5663 Roo.data.MemoryProxy = function(data){
5664     if (data.data) {
5665         data = data.data;
5666     }
5667     Roo.data.MemoryProxy.superclass.constructor.call(this);
5668     this.data = data;
5669 };
5670
5671 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5672     /**
5673      * Load data from the requested source (in this case an in-memory
5674      * data object passed to the constructor), read the data object into
5675      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5676      * process that block using the passed callback.
5677      * @param {Object} params This parameter is not used by the MemoryProxy class.
5678      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5679      * object into a block of Roo.data.Records.
5680      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5681      * The function must be passed <ul>
5682      * <li>The Record block object</li>
5683      * <li>The "arg" argument from the load function</li>
5684      * <li>A boolean success indicator</li>
5685      * </ul>
5686      * @param {Object} scope The scope in which to call the callback
5687      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5688      */
5689     load : function(params, reader, callback, scope, arg){
5690         params = params || {};
5691         var result;
5692         try {
5693             result = reader.readRecords(this.data);
5694         }catch(e){
5695             this.fireEvent("loadexception", this, arg, null, e);
5696             callback.call(scope, null, arg, false);
5697             return;
5698         }
5699         callback.call(scope, result, arg, true);
5700     },
5701     
5702     // private
5703     update : function(params, records){
5704         
5705     }
5706 });/*
5707  * Based on:
5708  * Ext JS Library 1.1.1
5709  * Copyright(c) 2006-2007, Ext JS, LLC.
5710  *
5711  * Originally Released Under LGPL - original licence link has changed is not relivant.
5712  *
5713  * Fork - LGPL
5714  * <script type="text/javascript">
5715  */
5716 /**
5717  * @class Roo.data.HttpProxy
5718  * @extends Roo.data.DataProxy
5719  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5720  * configured to reference a certain URL.<br><br>
5721  * <p>
5722  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5723  * from which the running page was served.<br><br>
5724  * <p>
5725  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5726  * <p>
5727  * Be aware that to enable the browser to parse an XML document, the server must set
5728  * the Content-Type header in the HTTP response to "text/xml".
5729  * @constructor
5730  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5731  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5732  * will be used to make the request.
5733  */
5734 Roo.data.HttpProxy = function(conn){
5735     Roo.data.HttpProxy.superclass.constructor.call(this);
5736     // is conn a conn config or a real conn?
5737     this.conn = conn;
5738     this.useAjax = !conn || !conn.events;
5739   
5740 };
5741
5742 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5743     // thse are take from connection...
5744     
5745     /**
5746      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5747      */
5748     /**
5749      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5750      * extra parameters to each request made by this object. (defaults to undefined)
5751      */
5752     /**
5753      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5754      *  to each request made by this object. (defaults to undefined)
5755      */
5756     /**
5757      * @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)
5758      */
5759     /**
5760      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5761      */
5762      /**
5763      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5764      * @type Boolean
5765      */
5766   
5767
5768     /**
5769      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5770      * @type Boolean
5771      */
5772     /**
5773      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5774      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5775      * a finer-grained basis than the DataProxy events.
5776      */
5777     getConnection : function(){
5778         return this.useAjax ? Roo.Ajax : this.conn;
5779     },
5780
5781     /**
5782      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5783      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5784      * process that block using the passed callback.
5785      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5786      * for the request to the remote server.
5787      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5788      * object into a block of Roo.data.Records.
5789      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5790      * The function must be passed <ul>
5791      * <li>The Record block object</li>
5792      * <li>The "arg" argument from the load function</li>
5793      * <li>A boolean success indicator</li>
5794      * </ul>
5795      * @param {Object} scope The scope in which to call the callback
5796      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5797      */
5798     load : function(params, reader, callback, scope, arg){
5799         if(this.fireEvent("beforeload", this, params) !== false){
5800             var  o = {
5801                 params : params || {},
5802                 request: {
5803                     callback : callback,
5804                     scope : scope,
5805                     arg : arg
5806                 },
5807                 reader: reader,
5808                 callback : this.loadResponse,
5809                 scope: this
5810             };
5811             if(this.useAjax){
5812                 Roo.applyIf(o, this.conn);
5813                 if(this.activeRequest){
5814                     Roo.Ajax.abort(this.activeRequest);
5815                 }
5816                 this.activeRequest = Roo.Ajax.request(o);
5817             }else{
5818                 this.conn.request(o);
5819             }
5820         }else{
5821             callback.call(scope||this, null, arg, false);
5822         }
5823     },
5824
5825     // private
5826     loadResponse : function(o, success, response){
5827         delete this.activeRequest;
5828         if(!success){
5829             this.fireEvent("loadexception", this, o, response);
5830             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5831             return;
5832         }
5833         var result;
5834         try {
5835             result = o.reader.read(response);
5836         }catch(e){
5837             this.fireEvent("loadexception", this, o, response, e);
5838             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5839             return;
5840         }
5841         
5842         this.fireEvent("load", this, o, o.request.arg);
5843         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5844     },
5845
5846     // private
5847     update : function(dataSet){
5848
5849     },
5850
5851     // private
5852     updateResponse : function(dataSet){
5853
5854     }
5855 });/*
5856  * Based on:
5857  * Ext JS Library 1.1.1
5858  * Copyright(c) 2006-2007, Ext JS, LLC.
5859  *
5860  * Originally Released Under LGPL - original licence link has changed is not relivant.
5861  *
5862  * Fork - LGPL
5863  * <script type="text/javascript">
5864  */
5865
5866 /**
5867  * @class Roo.data.ScriptTagProxy
5868  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5869  * other than the originating domain of the running page.<br><br>
5870  * <p>
5871  * <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
5872  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5873  * <p>
5874  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5875  * source code that is used as the source inside a &lt;script> tag.<br><br>
5876  * <p>
5877  * In order for the browser to process the returned data, the server must wrap the data object
5878  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5879  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5880  * depending on whether the callback name was passed:
5881  * <p>
5882  * <pre><code>
5883 boolean scriptTag = false;
5884 String cb = request.getParameter("callback");
5885 if (cb != null) {
5886     scriptTag = true;
5887     response.setContentType("text/javascript");
5888 } else {
5889     response.setContentType("application/x-json");
5890 }
5891 Writer out = response.getWriter();
5892 if (scriptTag) {
5893     out.write(cb + "(");
5894 }
5895 out.print(dataBlock.toJsonString());
5896 if (scriptTag) {
5897     out.write(");");
5898 }
5899 </pre></code>
5900  *
5901  * @constructor
5902  * @param {Object} config A configuration object.
5903  */
5904 Roo.data.ScriptTagProxy = function(config){
5905     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5906     Roo.apply(this, config);
5907     this.head = document.getElementsByTagName("head")[0];
5908 };
5909
5910 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5911
5912 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5913     /**
5914      * @cfg {String} url The URL from which to request the data object.
5915      */
5916     /**
5917      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5918      */
5919     timeout : 30000,
5920     /**
5921      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5922      * the server the name of the callback function set up by the load call to process the returned data object.
5923      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5924      * javascript output which calls this named function passing the data object as its only parameter.
5925      */
5926     callbackParam : "callback",
5927     /**
5928      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5929      * name to the request.
5930      */
5931     nocache : true,
5932
5933     /**
5934      * Load data from the configured URL, read the data object into
5935      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5936      * process that block using the passed callback.
5937      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5938      * for the request to the remote server.
5939      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5940      * object into a block of Roo.data.Records.
5941      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5942      * The function must be passed <ul>
5943      * <li>The Record block object</li>
5944      * <li>The "arg" argument from the load function</li>
5945      * <li>A boolean success indicator</li>
5946      * </ul>
5947      * @param {Object} scope The scope in which to call the callback
5948      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5949      */
5950     load : function(params, reader, callback, scope, arg){
5951         if(this.fireEvent("beforeload", this, params) !== false){
5952
5953             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5954
5955             var url = this.url;
5956             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5957             if(this.nocache){
5958                 url += "&_dc=" + (new Date().getTime());
5959             }
5960             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5961             var trans = {
5962                 id : transId,
5963                 cb : "stcCallback"+transId,
5964                 scriptId : "stcScript"+transId,
5965                 params : params,
5966                 arg : arg,
5967                 url : url,
5968                 callback : callback,
5969                 scope : scope,
5970                 reader : reader
5971             };
5972             var conn = this;
5973
5974             window[trans.cb] = function(o){
5975                 conn.handleResponse(o, trans);
5976             };
5977
5978             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5979
5980             if(this.autoAbort !== false){
5981                 this.abort();
5982             }
5983
5984             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5985
5986             var script = document.createElement("script");
5987             script.setAttribute("src", url);
5988             script.setAttribute("type", "text/javascript");
5989             script.setAttribute("id", trans.scriptId);
5990             this.head.appendChild(script);
5991
5992             this.trans = trans;
5993         }else{
5994             callback.call(scope||this, null, arg, false);
5995         }
5996     },
5997
5998     // private
5999     isLoading : function(){
6000         return this.trans ? true : false;
6001     },
6002
6003     /**
6004      * Abort the current server request.
6005      */
6006     abort : function(){
6007         if(this.isLoading()){
6008             this.destroyTrans(this.trans);
6009         }
6010     },
6011
6012     // private
6013     destroyTrans : function(trans, isLoaded){
6014         this.head.removeChild(document.getElementById(trans.scriptId));
6015         clearTimeout(trans.timeoutId);
6016         if(isLoaded){
6017             window[trans.cb] = undefined;
6018             try{
6019                 delete window[trans.cb];
6020             }catch(e){}
6021         }else{
6022             // if hasn't been loaded, wait for load to remove it to prevent script error
6023             window[trans.cb] = function(){
6024                 window[trans.cb] = undefined;
6025                 try{
6026                     delete window[trans.cb];
6027                 }catch(e){}
6028             };
6029         }
6030     },
6031
6032     // private
6033     handleResponse : function(o, trans){
6034         this.trans = false;
6035         this.destroyTrans(trans, true);
6036         var result;
6037         try {
6038             result = trans.reader.readRecords(o);
6039         }catch(e){
6040             this.fireEvent("loadexception", this, o, trans.arg, e);
6041             trans.callback.call(trans.scope||window, null, trans.arg, false);
6042             return;
6043         }
6044         this.fireEvent("load", this, o, trans.arg);
6045         trans.callback.call(trans.scope||window, result, trans.arg, true);
6046     },
6047
6048     // private
6049     handleFailure : function(trans){
6050         this.trans = false;
6051         this.destroyTrans(trans, false);
6052         this.fireEvent("loadexception", this, null, trans.arg);
6053         trans.callback.call(trans.scope||window, null, trans.arg, false);
6054     }
6055 });/*
6056  * Based on:
6057  * Ext JS Library 1.1.1
6058  * Copyright(c) 2006-2007, Ext JS, LLC.
6059  *
6060  * Originally Released Under LGPL - original licence link has changed is not relivant.
6061  *
6062  * Fork - LGPL
6063  * <script type="text/javascript">
6064  */
6065
6066 /**
6067  * @class Roo.data.JsonReader
6068  * @extends Roo.data.DataReader
6069  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6070  * based on mappings in a provided Roo.data.Record constructor.
6071  * 
6072  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6073  * in the reply previously. 
6074  * 
6075  * <p>
6076  * Example code:
6077  * <pre><code>
6078 var RecordDef = Roo.data.Record.create([
6079     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6080     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6081 ]);
6082 var myReader = new Roo.data.JsonReader({
6083     totalProperty: "results",    // The property which contains the total dataset size (optional)
6084     root: "rows",                // The property which contains an Array of row objects
6085     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6086 }, RecordDef);
6087 </code></pre>
6088  * <p>
6089  * This would consume a JSON file like this:
6090  * <pre><code>
6091 { 'results': 2, 'rows': [
6092     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6093     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6094 }
6095 </code></pre>
6096  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6097  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6098  * paged from the remote server.
6099  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6100  * @cfg {String} root name of the property which contains the Array of row objects.
6101  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6102  * @constructor
6103  * Create a new JsonReader
6104  * @param {Object} meta Metadata configuration options
6105  * @param {Object} recordType Either an Array of field definition objects,
6106  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6107  */
6108 Roo.data.JsonReader = function(meta, recordType){
6109     
6110     meta = meta || {};
6111     // set some defaults:
6112     Roo.applyIf(meta, {
6113         totalProperty: 'total',
6114         successProperty : 'success',
6115         root : 'data',
6116         id : 'id'
6117     });
6118     
6119     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6120 };
6121 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6122     
6123     /**
6124      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6125      * Used by Store query builder to append _requestMeta to params.
6126      * 
6127      */
6128     metaFromRemote : false,
6129     /**
6130      * This method is only used by a DataProxy which has retrieved data from a remote server.
6131      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6132      * @return {Object} data A data block which is used by an Roo.data.Store object as
6133      * a cache of Roo.data.Records.
6134      */
6135     read : function(response){
6136         var json = response.responseText;
6137        
6138         var o = /* eval:var:o */ eval("("+json+")");
6139         if(!o) {
6140             throw {message: "JsonReader.read: Json object not found"};
6141         }
6142         
6143         if(o.metaData){
6144             
6145             delete this.ef;
6146             this.metaFromRemote = true;
6147             this.meta = o.metaData;
6148             this.recordType = Roo.data.Record.create(o.metaData.fields);
6149             this.onMetaChange(this.meta, this.recordType, o);
6150         }
6151         return this.readRecords(o);
6152     },
6153
6154     // private function a store will implement
6155     onMetaChange : function(meta, recordType, o){
6156
6157     },
6158
6159     /**
6160          * @ignore
6161          */
6162     simpleAccess: function(obj, subsc) {
6163         return obj[subsc];
6164     },
6165
6166         /**
6167          * @ignore
6168          */
6169     getJsonAccessor: function(){
6170         var re = /[\[\.]/;
6171         return function(expr) {
6172             try {
6173                 return(re.test(expr))
6174                     ? new Function("obj", "return obj." + expr)
6175                     : function(obj){
6176                         return obj[expr];
6177                     };
6178             } catch(e){}
6179             return Roo.emptyFn;
6180         };
6181     }(),
6182
6183     /**
6184      * Create a data block containing Roo.data.Records from an XML document.
6185      * @param {Object} o An object which contains an Array of row objects in the property specified
6186      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6187      * which contains the total size of the dataset.
6188      * @return {Object} data A data block which is used by an Roo.data.Store object as
6189      * a cache of Roo.data.Records.
6190      */
6191     readRecords : function(o){
6192         /**
6193          * After any data loads, the raw JSON data is available for further custom processing.
6194          * @type Object
6195          */
6196         this.jsonData = o;
6197         var s = this.meta, Record = this.recordType,
6198             f = Record.prototype.fields, fi = f.items, fl = f.length;
6199
6200 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6201         if (!this.ef) {
6202             if(s.totalProperty) {
6203                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6204                 }
6205                 if(s.successProperty) {
6206                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6207                 }
6208                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6209                 if (s.id) {
6210                         var g = this.getJsonAccessor(s.id);
6211                         this.getId = function(rec) {
6212                                 var r = g(rec);
6213                                 return (r === undefined || r === "") ? null : r;
6214                         };
6215                 } else {
6216                         this.getId = function(){return null;};
6217                 }
6218             this.ef = [];
6219             for(var jj = 0; jj < fl; jj++){
6220                 f = fi[jj];
6221                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6222                 this.ef[jj] = this.getJsonAccessor(map);
6223             }
6224         }
6225
6226         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6227         if(s.totalProperty){
6228             var vt = parseInt(this.getTotal(o), 10);
6229             if(!isNaN(vt)){
6230                 totalRecords = vt;
6231             }
6232         }
6233         if(s.successProperty){
6234             var vs = this.getSuccess(o);
6235             if(vs === false || vs === 'false'){
6236                 success = false;
6237             }
6238         }
6239         var records = [];
6240             for(var i = 0; i < c; i++){
6241                     var n = root[i];
6242                 var values = {};
6243                 var id = this.getId(n);
6244                 for(var j = 0; j < fl; j++){
6245                     f = fi[j];
6246                 var v = this.ef[j](n);
6247                 if (!f.convert) {
6248                     Roo.log('missing convert for ' + f.name);
6249                     Roo.log(f);
6250                     continue;
6251                 }
6252                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6253                 }
6254                 var record = new Record(values, id);
6255                 record.json = n;
6256                 records[i] = record;
6257             }
6258             return {
6259                 success : success,
6260                 records : records,
6261                 totalRecords : totalRecords
6262             };
6263     }
6264 });/*
6265  * Based on:
6266  * Ext JS Library 1.1.1
6267  * Copyright(c) 2006-2007, Ext JS, LLC.
6268  *
6269  * Originally Released Under LGPL - original licence link has changed is not relivant.
6270  *
6271  * Fork - LGPL
6272  * <script type="text/javascript">
6273  */
6274
6275 /**
6276  * @class Roo.data.XmlReader
6277  * @extends Roo.data.DataReader
6278  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6279  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6280  * <p>
6281  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6282  * header in the HTTP response must be set to "text/xml".</em>
6283  * <p>
6284  * Example code:
6285  * <pre><code>
6286 var RecordDef = Roo.data.Record.create([
6287    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6288    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6289 ]);
6290 var myReader = new Roo.data.XmlReader({
6291    totalRecords: "results", // The element which contains the total dataset size (optional)
6292    record: "row",           // The repeated element which contains row information
6293    id: "id"                 // The element within the row that provides an ID for the record (optional)
6294 }, RecordDef);
6295 </code></pre>
6296  * <p>
6297  * This would consume an XML file like this:
6298  * <pre><code>
6299 &lt;?xml?>
6300 &lt;dataset>
6301  &lt;results>2&lt;/results>
6302  &lt;row>
6303    &lt;id>1&lt;/id>
6304    &lt;name>Bill&lt;/name>
6305    &lt;occupation>Gardener&lt;/occupation>
6306  &lt;/row>
6307  &lt;row>
6308    &lt;id>2&lt;/id>
6309    &lt;name>Ben&lt;/name>
6310    &lt;occupation>Horticulturalist&lt;/occupation>
6311  &lt;/row>
6312 &lt;/dataset>
6313 </code></pre>
6314  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6315  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6316  * paged from the remote server.
6317  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6318  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6319  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6320  * a record identifier value.
6321  * @constructor
6322  * Create a new XmlReader
6323  * @param {Object} meta Metadata configuration options
6324  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6325  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6326  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6327  */
6328 Roo.data.XmlReader = function(meta, recordType){
6329     meta = meta || {};
6330     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6331 };
6332 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6333     /**
6334      * This method is only used by a DataProxy which has retrieved data from a remote server.
6335          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6336          * to contain a method called 'responseXML' that returns an XML document object.
6337      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6338      * a cache of Roo.data.Records.
6339      */
6340     read : function(response){
6341         var doc = response.responseXML;
6342         if(!doc) {
6343             throw {message: "XmlReader.read: XML Document not available"};
6344         }
6345         return this.readRecords(doc);
6346     },
6347
6348     /**
6349      * Create a data block containing Roo.data.Records from an XML document.
6350          * @param {Object} doc A parsed XML document.
6351      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6352      * a cache of Roo.data.Records.
6353      */
6354     readRecords : function(doc){
6355         /**
6356          * After any data loads/reads, the raw XML Document is available for further custom processing.
6357          * @type XMLDocument
6358          */
6359         this.xmlData = doc;
6360         var root = doc.documentElement || doc;
6361         var q = Roo.DomQuery;
6362         var recordType = this.recordType, fields = recordType.prototype.fields;
6363         var sid = this.meta.id;
6364         var totalRecords = 0, success = true;
6365         if(this.meta.totalRecords){
6366             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6367         }
6368         
6369         if(this.meta.success){
6370             var sv = q.selectValue(this.meta.success, root, true);
6371             success = sv !== false && sv !== 'false';
6372         }
6373         var records = [];
6374         var ns = q.select(this.meta.record, root);
6375         for(var i = 0, len = ns.length; i < len; i++) {
6376                 var n = ns[i];
6377                 var values = {};
6378                 var id = sid ? q.selectValue(sid, n) : undefined;
6379                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6380                     var f = fields.items[j];
6381                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6382                     v = f.convert(v);
6383                     values[f.name] = v;
6384                 }
6385                 var record = new recordType(values, id);
6386                 record.node = n;
6387                 records[records.length] = record;
6388             }
6389
6390             return {
6391                 success : success,
6392                 records : records,
6393                 totalRecords : totalRecords || records.length
6394             };
6395     }
6396 });/*
6397  * Based on:
6398  * Ext JS Library 1.1.1
6399  * Copyright(c) 2006-2007, Ext JS, LLC.
6400  *
6401  * Originally Released Under LGPL - original licence link has changed is not relivant.
6402  *
6403  * Fork - LGPL
6404  * <script type="text/javascript">
6405  */
6406
6407 /**
6408  * @class Roo.data.ArrayReader
6409  * @extends Roo.data.DataReader
6410  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6411  * Each element of that Array represents a row of data fields. The
6412  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6413  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6414  * <p>
6415  * Example code:.
6416  * <pre><code>
6417 var RecordDef = Roo.data.Record.create([
6418     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6419     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6420 ]);
6421 var myReader = new Roo.data.ArrayReader({
6422     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6423 }, RecordDef);
6424 </code></pre>
6425  * <p>
6426  * This would consume an Array like this:
6427  * <pre><code>
6428 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6429   </code></pre>
6430  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6431  * @constructor
6432  * Create a new JsonReader
6433  * @param {Object} meta Metadata configuration options.
6434  * @param {Object} recordType Either an Array of field definition objects
6435  * as specified to {@link Roo.data.Record#create},
6436  * or an {@link Roo.data.Record} object
6437  * created using {@link Roo.data.Record#create}.
6438  */
6439 Roo.data.ArrayReader = function(meta, recordType){
6440     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6441 };
6442
6443 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6444     /**
6445      * Create a data block containing Roo.data.Records from an XML document.
6446      * @param {Object} o An Array of row objects which represents the dataset.
6447      * @return {Object} data A data block which is used by an Roo.data.Store object as
6448      * a cache of Roo.data.Records.
6449      */
6450     readRecords : function(o){
6451         var sid = this.meta ? this.meta.id : null;
6452         var recordType = this.recordType, fields = recordType.prototype.fields;
6453         var records = [];
6454         var root = o;
6455             for(var i = 0; i < root.length; i++){
6456                     var n = root[i];
6457                 var values = {};
6458                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6459                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6460                 var f = fields.items[j];
6461                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6462                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6463                 v = f.convert(v);
6464                 values[f.name] = v;
6465             }
6466                 var record = new recordType(values, id);
6467                 record.json = n;
6468                 records[records.length] = record;
6469             }
6470             return {
6471                 records : records,
6472                 totalRecords : records.length
6473             };
6474     }
6475 });/*
6476  * Based on:
6477  * Ext JS Library 1.1.1
6478  * Copyright(c) 2006-2007, Ext JS, LLC.
6479  *
6480  * Originally Released Under LGPL - original licence link has changed is not relivant.
6481  *
6482  * Fork - LGPL
6483  * <script type="text/javascript">
6484  */
6485
6486
6487 /**
6488  * @class Roo.data.Tree
6489  * @extends Roo.util.Observable
6490  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6491  * in the tree have most standard DOM functionality.
6492  * @constructor
6493  * @param {Node} root (optional) The root node
6494  */
6495 Roo.data.Tree = function(root){
6496    this.nodeHash = {};
6497    /**
6498     * The root node for this tree
6499     * @type Node
6500     */
6501    this.root = null;
6502    if(root){
6503        this.setRootNode(root);
6504    }
6505    this.addEvents({
6506        /**
6507         * @event append
6508         * Fires when a new child node is appended to a node in this tree.
6509         * @param {Tree} tree The owner tree
6510         * @param {Node} parent The parent node
6511         * @param {Node} node The newly appended node
6512         * @param {Number} index The index of the newly appended node
6513         */
6514        "append" : true,
6515        /**
6516         * @event remove
6517         * Fires when a child node is removed from a node in this tree.
6518         * @param {Tree} tree The owner tree
6519         * @param {Node} parent The parent node
6520         * @param {Node} node The child node removed
6521         */
6522        "remove" : true,
6523        /**
6524         * @event move
6525         * Fires when a node is moved to a new location in the tree
6526         * @param {Tree} tree The owner tree
6527         * @param {Node} node The node moved
6528         * @param {Node} oldParent The old parent of this node
6529         * @param {Node} newParent The new parent of this node
6530         * @param {Number} index The index it was moved to
6531         */
6532        "move" : true,
6533        /**
6534         * @event insert
6535         * Fires when a new child node is inserted in a node in this tree.
6536         * @param {Tree} tree The owner tree
6537         * @param {Node} parent The parent node
6538         * @param {Node} node The child node inserted
6539         * @param {Node} refNode The child node the node was inserted before
6540         */
6541        "insert" : true,
6542        /**
6543         * @event beforeappend
6544         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6545         * @param {Tree} tree The owner tree
6546         * @param {Node} parent The parent node
6547         * @param {Node} node The child node to be appended
6548         */
6549        "beforeappend" : true,
6550        /**
6551         * @event beforeremove
6552         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6553         * @param {Tree} tree The owner tree
6554         * @param {Node} parent The parent node
6555         * @param {Node} node The child node to be removed
6556         */
6557        "beforeremove" : true,
6558        /**
6559         * @event beforemove
6560         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6561         * @param {Tree} tree The owner tree
6562         * @param {Node} node The node being moved
6563         * @param {Node} oldParent The parent of the node
6564         * @param {Node} newParent The new parent the node is moving to
6565         * @param {Number} index The index it is being moved to
6566         */
6567        "beforemove" : true,
6568        /**
6569         * @event beforeinsert
6570         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6571         * @param {Tree} tree The owner tree
6572         * @param {Node} parent The parent node
6573         * @param {Node} node The child node to be inserted
6574         * @param {Node} refNode The child node the node is being inserted before
6575         */
6576        "beforeinsert" : true
6577    });
6578
6579     Roo.data.Tree.superclass.constructor.call(this);
6580 };
6581
6582 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6583     pathSeparator: "/",
6584
6585     proxyNodeEvent : function(){
6586         return this.fireEvent.apply(this, arguments);
6587     },
6588
6589     /**
6590      * Returns the root node for this tree.
6591      * @return {Node}
6592      */
6593     getRootNode : function(){
6594         return this.root;
6595     },
6596
6597     /**
6598      * Sets the root node for this tree.
6599      * @param {Node} node
6600      * @return {Node}
6601      */
6602     setRootNode : function(node){
6603         this.root = node;
6604         node.ownerTree = this;
6605         node.isRoot = true;
6606         this.registerNode(node);
6607         return node;
6608     },
6609
6610     /**
6611      * Gets a node in this tree by its id.
6612      * @param {String} id
6613      * @return {Node}
6614      */
6615     getNodeById : function(id){
6616         return this.nodeHash[id];
6617     },
6618
6619     registerNode : function(node){
6620         this.nodeHash[node.id] = node;
6621     },
6622
6623     unregisterNode : function(node){
6624         delete this.nodeHash[node.id];
6625     },
6626
6627     toString : function(){
6628         return "[Tree"+(this.id?" "+this.id:"")+"]";
6629     }
6630 });
6631
6632 /**
6633  * @class Roo.data.Node
6634  * @extends Roo.util.Observable
6635  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6636  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6637  * @constructor
6638  * @param {Object} attributes The attributes/config for the node
6639  */
6640 Roo.data.Node = function(attributes){
6641     /**
6642      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6643      * @type {Object}
6644      */
6645     this.attributes = attributes || {};
6646     this.leaf = this.attributes.leaf;
6647     /**
6648      * The node id. @type String
6649      */
6650     this.id = this.attributes.id;
6651     if(!this.id){
6652         this.id = Roo.id(null, "ynode-");
6653         this.attributes.id = this.id;
6654     }
6655     /**
6656      * All child nodes of this node. @type Array
6657      */
6658     this.childNodes = [];
6659     if(!this.childNodes.indexOf){ // indexOf is a must
6660         this.childNodes.indexOf = function(o){
6661             for(var i = 0, len = this.length; i < len; i++){
6662                 if(this[i] == o) {
6663                     return i;
6664                 }
6665             }
6666             return -1;
6667         };
6668     }
6669     /**
6670      * The parent node for this node. @type Node
6671      */
6672     this.parentNode = null;
6673     /**
6674      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6675      */
6676     this.firstChild = null;
6677     /**
6678      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6679      */
6680     this.lastChild = null;
6681     /**
6682      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6683      */
6684     this.previousSibling = null;
6685     /**
6686      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6687      */
6688     this.nextSibling = null;
6689
6690     this.addEvents({
6691        /**
6692         * @event append
6693         * Fires when a new child node is appended
6694         * @param {Tree} tree The owner tree
6695         * @param {Node} this This node
6696         * @param {Node} node The newly appended node
6697         * @param {Number} index The index of the newly appended node
6698         */
6699        "append" : true,
6700        /**
6701         * @event remove
6702         * Fires when a child node is removed
6703         * @param {Tree} tree The owner tree
6704         * @param {Node} this This node
6705         * @param {Node} node The removed node
6706         */
6707        "remove" : true,
6708        /**
6709         * @event move
6710         * Fires when this node is moved to a new location in the tree
6711         * @param {Tree} tree The owner tree
6712         * @param {Node} this This node
6713         * @param {Node} oldParent The old parent of this node
6714         * @param {Node} newParent The new parent of this node
6715         * @param {Number} index The index it was moved to
6716         */
6717        "move" : true,
6718        /**
6719         * @event insert
6720         * Fires when a new child node is inserted.
6721         * @param {Tree} tree The owner tree
6722         * @param {Node} this This node
6723         * @param {Node} node The child node inserted
6724         * @param {Node} refNode The child node the node was inserted before
6725         */
6726        "insert" : true,
6727        /**
6728         * @event beforeappend
6729         * Fires before a new child is appended, return false to cancel the append.
6730         * @param {Tree} tree The owner tree
6731         * @param {Node} this This node
6732         * @param {Node} node The child node to be appended
6733         */
6734        "beforeappend" : true,
6735        /**
6736         * @event beforeremove
6737         * Fires before a child is removed, return false to cancel the remove.
6738         * @param {Tree} tree The owner tree
6739         * @param {Node} this This node
6740         * @param {Node} node The child node to be removed
6741         */
6742        "beforeremove" : true,
6743        /**
6744         * @event beforemove
6745         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6746         * @param {Tree} tree The owner tree
6747         * @param {Node} this This node
6748         * @param {Node} oldParent The parent of this node
6749         * @param {Node} newParent The new parent this node is moving to
6750         * @param {Number} index The index it is being moved to
6751         */
6752        "beforemove" : true,
6753        /**
6754         * @event beforeinsert
6755         * Fires before a new child is inserted, return false to cancel the insert.
6756         * @param {Tree} tree The owner tree
6757         * @param {Node} this This node
6758         * @param {Node} node The child node to be inserted
6759         * @param {Node} refNode The child node the node is being inserted before
6760         */
6761        "beforeinsert" : true
6762    });
6763     this.listeners = this.attributes.listeners;
6764     Roo.data.Node.superclass.constructor.call(this);
6765 };
6766
6767 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6768     fireEvent : function(evtName){
6769         // first do standard event for this node
6770         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6771             return false;
6772         }
6773         // then bubble it up to the tree if the event wasn't cancelled
6774         var ot = this.getOwnerTree();
6775         if(ot){
6776             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6777                 return false;
6778             }
6779         }
6780         return true;
6781     },
6782
6783     /**
6784      * Returns true if this node is a leaf
6785      * @return {Boolean}
6786      */
6787     isLeaf : function(){
6788         return this.leaf === true;
6789     },
6790
6791     // private
6792     setFirstChild : function(node){
6793         this.firstChild = node;
6794     },
6795
6796     //private
6797     setLastChild : function(node){
6798         this.lastChild = node;
6799     },
6800
6801
6802     /**
6803      * Returns true if this node is the last child of its parent
6804      * @return {Boolean}
6805      */
6806     isLast : function(){
6807        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6808     },
6809
6810     /**
6811      * Returns true if this node is the first child of its parent
6812      * @return {Boolean}
6813      */
6814     isFirst : function(){
6815        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6816     },
6817
6818     hasChildNodes : function(){
6819         return !this.isLeaf() && this.childNodes.length > 0;
6820     },
6821
6822     /**
6823      * Insert node(s) as the last child node of this node.
6824      * @param {Node/Array} node The node or Array of nodes to append
6825      * @return {Node} The appended node if single append, or null if an array was passed
6826      */
6827     appendChild : function(node){
6828         var multi = false;
6829         if(node instanceof Array){
6830             multi = node;
6831         }else if(arguments.length > 1){
6832             multi = arguments;
6833         }
6834         // if passed an array or multiple args do them one by one
6835         if(multi){
6836             for(var i = 0, len = multi.length; i < len; i++) {
6837                 this.appendChild(multi[i]);
6838             }
6839         }else{
6840             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6841                 return false;
6842             }
6843             var index = this.childNodes.length;
6844             var oldParent = node.parentNode;
6845             // it's a move, make sure we move it cleanly
6846             if(oldParent){
6847                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6848                     return false;
6849                 }
6850                 oldParent.removeChild(node);
6851             }
6852             index = this.childNodes.length;
6853             if(index == 0){
6854                 this.setFirstChild(node);
6855             }
6856             this.childNodes.push(node);
6857             node.parentNode = this;
6858             var ps = this.childNodes[index-1];
6859             if(ps){
6860                 node.previousSibling = ps;
6861                 ps.nextSibling = node;
6862             }else{
6863                 node.previousSibling = null;
6864             }
6865             node.nextSibling = null;
6866             this.setLastChild(node);
6867             node.setOwnerTree(this.getOwnerTree());
6868             this.fireEvent("append", this.ownerTree, this, node, index);
6869             if(oldParent){
6870                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6871             }
6872             return node;
6873         }
6874     },
6875
6876     /**
6877      * Removes a child node from this node.
6878      * @param {Node} node The node to remove
6879      * @return {Node} The removed node
6880      */
6881     removeChild : function(node){
6882         var index = this.childNodes.indexOf(node);
6883         if(index == -1){
6884             return false;
6885         }
6886         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6887             return false;
6888         }
6889
6890         // remove it from childNodes collection
6891         this.childNodes.splice(index, 1);
6892
6893         // update siblings
6894         if(node.previousSibling){
6895             node.previousSibling.nextSibling = node.nextSibling;
6896         }
6897         if(node.nextSibling){
6898             node.nextSibling.previousSibling = node.previousSibling;
6899         }
6900
6901         // update child refs
6902         if(this.firstChild == node){
6903             this.setFirstChild(node.nextSibling);
6904         }
6905         if(this.lastChild == node){
6906             this.setLastChild(node.previousSibling);
6907         }
6908
6909         node.setOwnerTree(null);
6910         // clear any references from the node
6911         node.parentNode = null;
6912         node.previousSibling = null;
6913         node.nextSibling = null;
6914         this.fireEvent("remove", this.ownerTree, this, node);
6915         return node;
6916     },
6917
6918     /**
6919      * Inserts the first node before the second node in this nodes childNodes collection.
6920      * @param {Node} node The node to insert
6921      * @param {Node} refNode The node to insert before (if null the node is appended)
6922      * @return {Node} The inserted node
6923      */
6924     insertBefore : function(node, refNode){
6925         if(!refNode){ // like standard Dom, refNode can be null for append
6926             return this.appendChild(node);
6927         }
6928         // nothing to do
6929         if(node == refNode){
6930             return false;
6931         }
6932
6933         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6934             return false;
6935         }
6936         var index = this.childNodes.indexOf(refNode);
6937         var oldParent = node.parentNode;
6938         var refIndex = index;
6939
6940         // when moving internally, indexes will change after remove
6941         if(oldParent == this && this.childNodes.indexOf(node) < index){
6942             refIndex--;
6943         }
6944
6945         // it's a move, make sure we move it cleanly
6946         if(oldParent){
6947             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6948                 return false;
6949             }
6950             oldParent.removeChild(node);
6951         }
6952         if(refIndex == 0){
6953             this.setFirstChild(node);
6954         }
6955         this.childNodes.splice(refIndex, 0, node);
6956         node.parentNode = this;
6957         var ps = this.childNodes[refIndex-1];
6958         if(ps){
6959             node.previousSibling = ps;
6960             ps.nextSibling = node;
6961         }else{
6962             node.previousSibling = null;
6963         }
6964         node.nextSibling = refNode;
6965         refNode.previousSibling = node;
6966         node.setOwnerTree(this.getOwnerTree());
6967         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6968         if(oldParent){
6969             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6970         }
6971         return node;
6972     },
6973
6974     /**
6975      * Returns the child node at the specified index.
6976      * @param {Number} index
6977      * @return {Node}
6978      */
6979     item : function(index){
6980         return this.childNodes[index];
6981     },
6982
6983     /**
6984      * Replaces one child node in this node with another.
6985      * @param {Node} newChild The replacement node
6986      * @param {Node} oldChild The node to replace
6987      * @return {Node} The replaced node
6988      */
6989     replaceChild : function(newChild, oldChild){
6990         this.insertBefore(newChild, oldChild);
6991         this.removeChild(oldChild);
6992         return oldChild;
6993     },
6994
6995     /**
6996      * Returns the index of a child node
6997      * @param {Node} node
6998      * @return {Number} The index of the node or -1 if it was not found
6999      */
7000     indexOf : function(child){
7001         return this.childNodes.indexOf(child);
7002     },
7003
7004     /**
7005      * Returns the tree this node is in.
7006      * @return {Tree}
7007      */
7008     getOwnerTree : function(){
7009         // if it doesn't have one, look for one
7010         if(!this.ownerTree){
7011             var p = this;
7012             while(p){
7013                 if(p.ownerTree){
7014                     this.ownerTree = p.ownerTree;
7015                     break;
7016                 }
7017                 p = p.parentNode;
7018             }
7019         }
7020         return this.ownerTree;
7021     },
7022
7023     /**
7024      * Returns depth of this node (the root node has a depth of 0)
7025      * @return {Number}
7026      */
7027     getDepth : function(){
7028         var depth = 0;
7029         var p = this;
7030         while(p.parentNode){
7031             ++depth;
7032             p = p.parentNode;
7033         }
7034         return depth;
7035     },
7036
7037     // private
7038     setOwnerTree : function(tree){
7039         // if it's move, we need to update everyone
7040         if(tree != this.ownerTree){
7041             if(this.ownerTree){
7042                 this.ownerTree.unregisterNode(this);
7043             }
7044             this.ownerTree = tree;
7045             var cs = this.childNodes;
7046             for(var i = 0, len = cs.length; i < len; i++) {
7047                 cs[i].setOwnerTree(tree);
7048             }
7049             if(tree){
7050                 tree.registerNode(this);
7051             }
7052         }
7053     },
7054
7055     /**
7056      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7057      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7058      * @return {String} The path
7059      */
7060     getPath : function(attr){
7061         attr = attr || "id";
7062         var p = this.parentNode;
7063         var b = [this.attributes[attr]];
7064         while(p){
7065             b.unshift(p.attributes[attr]);
7066             p = p.parentNode;
7067         }
7068         var sep = this.getOwnerTree().pathSeparator;
7069         return sep + b.join(sep);
7070     },
7071
7072     /**
7073      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7074      * function call will be the scope provided or the current node. The arguments to the function
7075      * will be the args provided or the current node. If the function returns false at any point,
7076      * the bubble is stopped.
7077      * @param {Function} fn The function to call
7078      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7079      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7080      */
7081     bubble : function(fn, scope, args){
7082         var p = this;
7083         while(p){
7084             if(fn.call(scope || p, args || p) === false){
7085                 break;
7086             }
7087             p = p.parentNode;
7088         }
7089     },
7090
7091     /**
7092      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7093      * function call will be the scope provided or the current node. The arguments to the function
7094      * will be the args provided or the current node. If the function returns false at any point,
7095      * the cascade is stopped on that branch.
7096      * @param {Function} fn The function to call
7097      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7098      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7099      */
7100     cascade : function(fn, scope, args){
7101         if(fn.call(scope || this, args || this) !== false){
7102             var cs = this.childNodes;
7103             for(var i = 0, len = cs.length; i < len; i++) {
7104                 cs[i].cascade(fn, scope, args);
7105             }
7106         }
7107     },
7108
7109     /**
7110      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7111      * function call will be the scope provided or the current node. The arguments to the function
7112      * will be the args provided or the current node. If the function returns false at any point,
7113      * the iteration stops.
7114      * @param {Function} fn The function to call
7115      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7116      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7117      */
7118     eachChild : function(fn, scope, args){
7119         var cs = this.childNodes;
7120         for(var i = 0, len = cs.length; i < len; i++) {
7121                 if(fn.call(scope || this, args || cs[i]) === false){
7122                     break;
7123                 }
7124         }
7125     },
7126
7127     /**
7128      * Finds the first child that has the attribute with the specified value.
7129      * @param {String} attribute The attribute name
7130      * @param {Mixed} value The value to search for
7131      * @return {Node} The found child or null if none was found
7132      */
7133     findChild : function(attribute, value){
7134         var cs = this.childNodes;
7135         for(var i = 0, len = cs.length; i < len; i++) {
7136                 if(cs[i].attributes[attribute] == value){
7137                     return cs[i];
7138                 }
7139         }
7140         return null;
7141     },
7142
7143     /**
7144      * Finds the first child by a custom function. The child matches if the function passed
7145      * returns true.
7146      * @param {Function} fn
7147      * @param {Object} scope (optional)
7148      * @return {Node} The found child or null if none was found
7149      */
7150     findChildBy : function(fn, scope){
7151         var cs = this.childNodes;
7152         for(var i = 0, len = cs.length; i < len; i++) {
7153                 if(fn.call(scope||cs[i], cs[i]) === true){
7154                     return cs[i];
7155                 }
7156         }
7157         return null;
7158     },
7159
7160     /**
7161      * Sorts this nodes children using the supplied sort function
7162      * @param {Function} fn
7163      * @param {Object} scope (optional)
7164      */
7165     sort : function(fn, scope){
7166         var cs = this.childNodes;
7167         var len = cs.length;
7168         if(len > 0){
7169             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7170             cs.sort(sortFn);
7171             for(var i = 0; i < len; i++){
7172                 var n = cs[i];
7173                 n.previousSibling = cs[i-1];
7174                 n.nextSibling = cs[i+1];
7175                 if(i == 0){
7176                     this.setFirstChild(n);
7177                 }
7178                 if(i == len-1){
7179                     this.setLastChild(n);
7180                 }
7181             }
7182         }
7183     },
7184
7185     /**
7186      * Returns true if this node is an ancestor (at any point) of the passed node.
7187      * @param {Node} node
7188      * @return {Boolean}
7189      */
7190     contains : function(node){
7191         return node.isAncestor(this);
7192     },
7193
7194     /**
7195      * Returns true if the passed node is an ancestor (at any point) of this node.
7196      * @param {Node} node
7197      * @return {Boolean}
7198      */
7199     isAncestor : function(node){
7200         var p = this.parentNode;
7201         while(p){
7202             if(p == node){
7203                 return true;
7204             }
7205             p = p.parentNode;
7206         }
7207         return false;
7208     },
7209
7210     toString : function(){
7211         return "[Node"+(this.id?" "+this.id:"")+"]";
7212     }
7213 });/*
7214  * Based on:
7215  * Ext JS Library 1.1.1
7216  * Copyright(c) 2006-2007, Ext JS, LLC.
7217  *
7218  * Originally Released Under LGPL - original licence link has changed is not relivant.
7219  *
7220  * Fork - LGPL
7221  * <script type="text/javascript">
7222  */
7223  
7224
7225 /**
7226  * @class Roo.ComponentMgr
7227  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7228  * @singleton
7229  */
7230 Roo.ComponentMgr = function(){
7231     var all = new Roo.util.MixedCollection();
7232
7233     return {
7234         /**
7235          * Registers a component.
7236          * @param {Roo.Component} c The component
7237          */
7238         register : function(c){
7239             all.add(c);
7240         },
7241
7242         /**
7243          * Unregisters a component.
7244          * @param {Roo.Component} c The component
7245          */
7246         unregister : function(c){
7247             all.remove(c);
7248         },
7249
7250         /**
7251          * Returns a component by id
7252          * @param {String} id The component id
7253          */
7254         get : function(id){
7255             return all.get(id);
7256         },
7257
7258         /**
7259          * Registers a function that will be called when a specified component is added to ComponentMgr
7260          * @param {String} id The component id
7261          * @param {Funtction} fn The callback function
7262          * @param {Object} scope The scope of the callback
7263          */
7264         onAvailable : function(id, fn, scope){
7265             all.on("add", function(index, o){
7266                 if(o.id == id){
7267                     fn.call(scope || o, o);
7268                     all.un("add", fn, scope);
7269                 }
7270             });
7271         }
7272     };
7273 }();/*
7274  * Based on:
7275  * Ext JS Library 1.1.1
7276  * Copyright(c) 2006-2007, Ext JS, LLC.
7277  *
7278  * Originally Released Under LGPL - original licence link has changed is not relivant.
7279  *
7280  * Fork - LGPL
7281  * <script type="text/javascript">
7282  */
7283  
7284 /**
7285  * @class Roo.Component
7286  * @extends Roo.util.Observable
7287  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7288  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7289  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7290  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7291  * All visual components (widgets) that require rendering into a layout should subclass Component.
7292  * @constructor
7293  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7294  * 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
7295  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7296  */
7297 Roo.Component = function(config){
7298     config = config || {};
7299     if(config.tagName || config.dom || typeof config == "string"){ // element object
7300         config = {el: config, id: config.id || config};
7301     }
7302     this.initialConfig = config;
7303
7304     Roo.apply(this, config);
7305     this.addEvents({
7306         /**
7307          * @event disable
7308          * Fires after the component is disabled.
7309              * @param {Roo.Component} this
7310              */
7311         disable : true,
7312         /**
7313          * @event enable
7314          * Fires after the component is enabled.
7315              * @param {Roo.Component} this
7316              */
7317         enable : true,
7318         /**
7319          * @event beforeshow
7320          * Fires before the component is shown.  Return false to stop the show.
7321              * @param {Roo.Component} this
7322              */
7323         beforeshow : true,
7324         /**
7325          * @event show
7326          * Fires after the component is shown.
7327              * @param {Roo.Component} this
7328              */
7329         show : true,
7330         /**
7331          * @event beforehide
7332          * Fires before the component is hidden. Return false to stop the hide.
7333              * @param {Roo.Component} this
7334              */
7335         beforehide : true,
7336         /**
7337          * @event hide
7338          * Fires after the component is hidden.
7339              * @param {Roo.Component} this
7340              */
7341         hide : true,
7342         /**
7343          * @event beforerender
7344          * Fires before the component is rendered. Return false to stop the render.
7345              * @param {Roo.Component} this
7346              */
7347         beforerender : true,
7348         /**
7349          * @event render
7350          * Fires after the component is rendered.
7351              * @param {Roo.Component} this
7352              */
7353         render : true,
7354         /**
7355          * @event beforedestroy
7356          * Fires before the component is destroyed. Return false to stop the destroy.
7357              * @param {Roo.Component} this
7358              */
7359         beforedestroy : true,
7360         /**
7361          * @event destroy
7362          * Fires after the component is destroyed.
7363              * @param {Roo.Component} this
7364              */
7365         destroy : true
7366     });
7367     if(!this.id){
7368         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7369     }
7370     Roo.ComponentMgr.register(this);
7371     Roo.Component.superclass.constructor.call(this);
7372     this.initComponent();
7373     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7374         this.render(this.renderTo);
7375         delete this.renderTo;
7376     }
7377 };
7378
7379 // private
7380 Roo.Component.AUTO_ID = 1000;
7381
7382 Roo.extend(Roo.Component, Roo.util.Observable, {
7383     /**
7384      * @property {Boolean} hidden
7385      * true if this component is hidden. Read-only.
7386      */
7387     hidden : false,
7388     /**
7389      * true if this component is disabled. Read-only.
7390      */
7391     disabled : false,
7392     /**
7393      * true if this component has been rendered. Read-only.
7394      */
7395     rendered : false,
7396     
7397     /** @cfg {String} disableClass
7398      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7399      */
7400     disabledClass : "x-item-disabled",
7401         /** @cfg {Boolean} allowDomMove
7402          * Whether the component can move the Dom node when rendering (defaults to true).
7403          */
7404     allowDomMove : true,
7405     /** @cfg {String} hideMode
7406      * How this component should hidden. Supported values are
7407      * "visibility" (css visibility), "offsets" (negative offset position) and
7408      * "display" (css display) - defaults to "display".
7409      */
7410     hideMode: 'display',
7411
7412     // private
7413     ctype : "Roo.Component",
7414
7415     /** @cfg {String} actionMode 
7416      * which property holds the element that used for  hide() / show() / disable() / enable()
7417      * default is 'el' 
7418      */
7419     actionMode : "el",
7420
7421     // private
7422     getActionEl : function(){
7423         return this[this.actionMode];
7424     },
7425
7426     initComponent : Roo.emptyFn,
7427     /**
7428      * If this is a lazy rendering component, render it to its container element.
7429      * @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.
7430      */
7431     render : function(container, position){
7432         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7433             if(!container && this.el){
7434                 this.el = Roo.get(this.el);
7435                 container = this.el.dom.parentNode;
7436                 this.allowDomMove = false;
7437             }
7438             this.container = Roo.get(container);
7439             this.rendered = true;
7440             if(position !== undefined){
7441                 if(typeof position == 'number'){
7442                     position = this.container.dom.childNodes[position];
7443                 }else{
7444                     position = Roo.getDom(position);
7445                 }
7446             }
7447             this.onRender(this.container, position || null);
7448             if(this.cls){
7449                 this.el.addClass(this.cls);
7450                 delete this.cls;
7451             }
7452             if(this.style){
7453                 this.el.applyStyles(this.style);
7454                 delete this.style;
7455             }
7456             this.fireEvent("render", this);
7457             this.afterRender(this.container);
7458             if(this.hidden){
7459                 this.hide();
7460             }
7461             if(this.disabled){
7462                 this.disable();
7463             }
7464         }
7465         return this;
7466     },
7467
7468     // private
7469     // default function is not really useful
7470     onRender : function(ct, position){
7471         if(this.el){
7472             this.el = Roo.get(this.el);
7473             if(this.allowDomMove !== false){
7474                 ct.dom.insertBefore(this.el.dom, position);
7475             }
7476         }
7477     },
7478
7479     // private
7480     getAutoCreate : function(){
7481         var cfg = typeof this.autoCreate == "object" ?
7482                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7483         if(this.id && !cfg.id){
7484             cfg.id = this.id;
7485         }
7486         return cfg;
7487     },
7488
7489     // private
7490     afterRender : Roo.emptyFn,
7491
7492     /**
7493      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7494      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7495      */
7496     destroy : function(){
7497         if(this.fireEvent("beforedestroy", this) !== false){
7498             this.purgeListeners();
7499             this.beforeDestroy();
7500             if(this.rendered){
7501                 this.el.removeAllListeners();
7502                 this.el.remove();
7503                 if(this.actionMode == "container"){
7504                     this.container.remove();
7505                 }
7506             }
7507             this.onDestroy();
7508             Roo.ComponentMgr.unregister(this);
7509             this.fireEvent("destroy", this);
7510         }
7511     },
7512
7513         // private
7514     beforeDestroy : function(){
7515
7516     },
7517
7518         // private
7519         onDestroy : function(){
7520
7521     },
7522
7523     /**
7524      * Returns the underlying {@link Roo.Element}.
7525      * @return {Roo.Element} The element
7526      */
7527     getEl : function(){
7528         return this.el;
7529     },
7530
7531     /**
7532      * Returns the id of this component.
7533      * @return {String}
7534      */
7535     getId : function(){
7536         return this.id;
7537     },
7538
7539     /**
7540      * Try to focus this component.
7541      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7542      * @return {Roo.Component} this
7543      */
7544     focus : function(selectText){
7545         if(this.rendered){
7546             this.el.focus();
7547             if(selectText === true){
7548                 this.el.dom.select();
7549             }
7550         }
7551         return this;
7552     },
7553
7554     // private
7555     blur : function(){
7556         if(this.rendered){
7557             this.el.blur();
7558         }
7559         return this;
7560     },
7561
7562     /**
7563      * Disable this component.
7564      * @return {Roo.Component} this
7565      */
7566     disable : function(){
7567         if(this.rendered){
7568             this.onDisable();
7569         }
7570         this.disabled = true;
7571         this.fireEvent("disable", this);
7572         return this;
7573     },
7574
7575         // private
7576     onDisable : function(){
7577         this.getActionEl().addClass(this.disabledClass);
7578         this.el.dom.disabled = true;
7579     },
7580
7581     /**
7582      * Enable this component.
7583      * @return {Roo.Component} this
7584      */
7585     enable : function(){
7586         if(this.rendered){
7587             this.onEnable();
7588         }
7589         this.disabled = false;
7590         this.fireEvent("enable", this);
7591         return this;
7592     },
7593
7594         // private
7595     onEnable : function(){
7596         this.getActionEl().removeClass(this.disabledClass);
7597         this.el.dom.disabled = false;
7598     },
7599
7600     /**
7601      * Convenience function for setting disabled/enabled by boolean.
7602      * @param {Boolean} disabled
7603      */
7604     setDisabled : function(disabled){
7605         this[disabled ? "disable" : "enable"]();
7606     },
7607
7608     /**
7609      * Show this component.
7610      * @return {Roo.Component} this
7611      */
7612     show: function(){
7613         if(this.fireEvent("beforeshow", this) !== false){
7614             this.hidden = false;
7615             if(this.rendered){
7616                 this.onShow();
7617             }
7618             this.fireEvent("show", this);
7619         }
7620         return this;
7621     },
7622
7623     // private
7624     onShow : function(){
7625         var ae = this.getActionEl();
7626         if(this.hideMode == 'visibility'){
7627             ae.dom.style.visibility = "visible";
7628         }else if(this.hideMode == 'offsets'){
7629             ae.removeClass('x-hidden');
7630         }else{
7631             ae.dom.style.display = "";
7632         }
7633     },
7634
7635     /**
7636      * Hide this component.
7637      * @return {Roo.Component} this
7638      */
7639     hide: function(){
7640         if(this.fireEvent("beforehide", this) !== false){
7641             this.hidden = true;
7642             if(this.rendered){
7643                 this.onHide();
7644             }
7645             this.fireEvent("hide", this);
7646         }
7647         return this;
7648     },
7649
7650     // private
7651     onHide : function(){
7652         var ae = this.getActionEl();
7653         if(this.hideMode == 'visibility'){
7654             ae.dom.style.visibility = "hidden";
7655         }else if(this.hideMode == 'offsets'){
7656             ae.addClass('x-hidden');
7657         }else{
7658             ae.dom.style.display = "none";
7659         }
7660     },
7661
7662     /**
7663      * Convenience function to hide or show this component by boolean.
7664      * @param {Boolean} visible True to show, false to hide
7665      * @return {Roo.Component} this
7666      */
7667     setVisible: function(visible){
7668         if(visible) {
7669             this.show();
7670         }else{
7671             this.hide();
7672         }
7673         return this;
7674     },
7675
7676     /**
7677      * Returns true if this component is visible.
7678      */
7679     isVisible : function(){
7680         return this.getActionEl().isVisible();
7681     },
7682
7683     cloneConfig : function(overrides){
7684         overrides = overrides || {};
7685         var id = overrides.id || Roo.id();
7686         var cfg = Roo.applyIf(overrides, this.initialConfig);
7687         cfg.id = id; // prevent dup id
7688         return new this.constructor(cfg);
7689     }
7690 });/*
7691  * Based on:
7692  * Ext JS Library 1.1.1
7693  * Copyright(c) 2006-2007, Ext JS, LLC.
7694  *
7695  * Originally Released Under LGPL - original licence link has changed is not relivant.
7696  *
7697  * Fork - LGPL
7698  * <script type="text/javascript">
7699  */
7700  (function(){ 
7701 /**
7702  * @class Roo.Layer
7703  * @extends Roo.Element
7704  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7705  * automatic maintaining of shadow/shim positions.
7706  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7707  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7708  * you can pass a string with a CSS class name. False turns off the shadow.
7709  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7710  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7711  * @cfg {String} cls CSS class to add to the element
7712  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7713  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7714  * @constructor
7715  * @param {Object} config An object with config options.
7716  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7717  */
7718
7719 Roo.Layer = function(config, existingEl){
7720     config = config || {};
7721     var dh = Roo.DomHelper;
7722     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7723     if(existingEl){
7724         this.dom = Roo.getDom(existingEl);
7725     }
7726     if(!this.dom){
7727         var o = config.dh || {tag: "div", cls: "x-layer"};
7728         this.dom = dh.append(pel, o);
7729     }
7730     if(config.cls){
7731         this.addClass(config.cls);
7732     }
7733     this.constrain = config.constrain !== false;
7734     this.visibilityMode = Roo.Element.VISIBILITY;
7735     if(config.id){
7736         this.id = this.dom.id = config.id;
7737     }else{
7738         this.id = Roo.id(this.dom);
7739     }
7740     this.zindex = config.zindex || this.getZIndex();
7741     this.position("absolute", this.zindex);
7742     if(config.shadow){
7743         this.shadowOffset = config.shadowOffset || 4;
7744         this.shadow = new Roo.Shadow({
7745             offset : this.shadowOffset,
7746             mode : config.shadow
7747         });
7748     }else{
7749         this.shadowOffset = 0;
7750     }
7751     this.useShim = config.shim !== false && Roo.useShims;
7752     this.useDisplay = config.useDisplay;
7753     this.hide();
7754 };
7755
7756 var supr = Roo.Element.prototype;
7757
7758 // shims are shared among layer to keep from having 100 iframes
7759 var shims = [];
7760
7761 Roo.extend(Roo.Layer, Roo.Element, {
7762
7763     getZIndex : function(){
7764         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7765     },
7766
7767     getShim : function(){
7768         if(!this.useShim){
7769             return null;
7770         }
7771         if(this.shim){
7772             return this.shim;
7773         }
7774         var shim = shims.shift();
7775         if(!shim){
7776             shim = this.createShim();
7777             shim.enableDisplayMode('block');
7778             shim.dom.style.display = 'none';
7779             shim.dom.style.visibility = 'visible';
7780         }
7781         var pn = this.dom.parentNode;
7782         if(shim.dom.parentNode != pn){
7783             pn.insertBefore(shim.dom, this.dom);
7784         }
7785         shim.setStyle('z-index', this.getZIndex()-2);
7786         this.shim = shim;
7787         return shim;
7788     },
7789
7790     hideShim : function(){
7791         if(this.shim){
7792             this.shim.setDisplayed(false);
7793             shims.push(this.shim);
7794             delete this.shim;
7795         }
7796     },
7797
7798     disableShadow : function(){
7799         if(this.shadow){
7800             this.shadowDisabled = true;
7801             this.shadow.hide();
7802             this.lastShadowOffset = this.shadowOffset;
7803             this.shadowOffset = 0;
7804         }
7805     },
7806
7807     enableShadow : function(show){
7808         if(this.shadow){
7809             this.shadowDisabled = false;
7810             this.shadowOffset = this.lastShadowOffset;
7811             delete this.lastShadowOffset;
7812             if(show){
7813                 this.sync(true);
7814             }
7815         }
7816     },
7817
7818     // private
7819     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7820     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7821     sync : function(doShow){
7822         var sw = this.shadow;
7823         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7824             var sh = this.getShim();
7825
7826             var w = this.getWidth(),
7827                 h = this.getHeight();
7828
7829             var l = this.getLeft(true),
7830                 t = this.getTop(true);
7831
7832             if(sw && !this.shadowDisabled){
7833                 if(doShow && !sw.isVisible()){
7834                     sw.show(this);
7835                 }else{
7836                     sw.realign(l, t, w, h);
7837                 }
7838                 if(sh){
7839                     if(doShow){
7840                        sh.show();
7841                     }
7842                     // fit the shim behind the shadow, so it is shimmed too
7843                     var a = sw.adjusts, s = sh.dom.style;
7844                     s.left = (Math.min(l, l+a.l))+"px";
7845                     s.top = (Math.min(t, t+a.t))+"px";
7846                     s.width = (w+a.w)+"px";
7847                     s.height = (h+a.h)+"px";
7848                 }
7849             }else if(sh){
7850                 if(doShow){
7851                    sh.show();
7852                 }
7853                 sh.setSize(w, h);
7854                 sh.setLeftTop(l, t);
7855             }
7856             
7857         }
7858     },
7859
7860     // private
7861     destroy : function(){
7862         this.hideShim();
7863         if(this.shadow){
7864             this.shadow.hide();
7865         }
7866         this.removeAllListeners();
7867         var pn = this.dom.parentNode;
7868         if(pn){
7869             pn.removeChild(this.dom);
7870         }
7871         Roo.Element.uncache(this.id);
7872     },
7873
7874     remove : function(){
7875         this.destroy();
7876     },
7877
7878     // private
7879     beginUpdate : function(){
7880         this.updating = true;
7881     },
7882
7883     // private
7884     endUpdate : function(){
7885         this.updating = false;
7886         this.sync(true);
7887     },
7888
7889     // private
7890     hideUnders : function(negOffset){
7891         if(this.shadow){
7892             this.shadow.hide();
7893         }
7894         this.hideShim();
7895     },
7896
7897     // private
7898     constrainXY : function(){
7899         if(this.constrain){
7900             var vw = Roo.lib.Dom.getViewWidth(),
7901                 vh = Roo.lib.Dom.getViewHeight();
7902             var s = Roo.get(document).getScroll();
7903
7904             var xy = this.getXY();
7905             var x = xy[0], y = xy[1];   
7906             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7907             // only move it if it needs it
7908             var moved = false;
7909             // first validate right/bottom
7910             if((x + w) > vw+s.left){
7911                 x = vw - w - this.shadowOffset;
7912                 moved = true;
7913             }
7914             if((y + h) > vh+s.top){
7915                 y = vh - h - this.shadowOffset;
7916                 moved = true;
7917             }
7918             // then make sure top/left isn't negative
7919             if(x < s.left){
7920                 x = s.left;
7921                 moved = true;
7922             }
7923             if(y < s.top){
7924                 y = s.top;
7925                 moved = true;
7926             }
7927             if(moved){
7928                 if(this.avoidY){
7929                     var ay = this.avoidY;
7930                     if(y <= ay && (y+h) >= ay){
7931                         y = ay-h-5;   
7932                     }
7933                 }
7934                 xy = [x, y];
7935                 this.storeXY(xy);
7936                 supr.setXY.call(this, xy);
7937                 this.sync();
7938             }
7939         }
7940     },
7941
7942     isVisible : function(){
7943         return this.visible;    
7944     },
7945
7946     // private
7947     showAction : function(){
7948         this.visible = true; // track visibility to prevent getStyle calls
7949         if(this.useDisplay === true){
7950             this.setDisplayed("");
7951         }else if(this.lastXY){
7952             supr.setXY.call(this, this.lastXY);
7953         }else if(this.lastLT){
7954             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7955         }
7956     },
7957
7958     // private
7959     hideAction : function(){
7960         this.visible = false;
7961         if(this.useDisplay === true){
7962             this.setDisplayed(false);
7963         }else{
7964             this.setLeftTop(-10000,-10000);
7965         }
7966     },
7967
7968     // overridden Element method
7969     setVisible : function(v, a, d, c, e){
7970         if(v){
7971             this.showAction();
7972         }
7973         if(a && v){
7974             var cb = function(){
7975                 this.sync(true);
7976                 if(c){
7977                     c();
7978                 }
7979             }.createDelegate(this);
7980             supr.setVisible.call(this, true, true, d, cb, e);
7981         }else{
7982             if(!v){
7983                 this.hideUnders(true);
7984             }
7985             var cb = c;
7986             if(a){
7987                 cb = function(){
7988                     this.hideAction();
7989                     if(c){
7990                         c();
7991                     }
7992                 }.createDelegate(this);
7993             }
7994             supr.setVisible.call(this, v, a, d, cb, e);
7995             if(v){
7996                 this.sync(true);
7997             }else if(!a){
7998                 this.hideAction();
7999             }
8000         }
8001     },
8002
8003     storeXY : function(xy){
8004         delete this.lastLT;
8005         this.lastXY = xy;
8006     },
8007
8008     storeLeftTop : function(left, top){
8009         delete this.lastXY;
8010         this.lastLT = [left, top];
8011     },
8012
8013     // private
8014     beforeFx : function(){
8015         this.beforeAction();
8016         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8017     },
8018
8019     // private
8020     afterFx : function(){
8021         Roo.Layer.superclass.afterFx.apply(this, arguments);
8022         this.sync(this.isVisible());
8023     },
8024
8025     // private
8026     beforeAction : function(){
8027         if(!this.updating && this.shadow){
8028             this.shadow.hide();
8029         }
8030     },
8031
8032     // overridden Element method
8033     setLeft : function(left){
8034         this.storeLeftTop(left, this.getTop(true));
8035         supr.setLeft.apply(this, arguments);
8036         this.sync();
8037     },
8038
8039     setTop : function(top){
8040         this.storeLeftTop(this.getLeft(true), top);
8041         supr.setTop.apply(this, arguments);
8042         this.sync();
8043     },
8044
8045     setLeftTop : function(left, top){
8046         this.storeLeftTop(left, top);
8047         supr.setLeftTop.apply(this, arguments);
8048         this.sync();
8049     },
8050
8051     setXY : function(xy, a, d, c, e){
8052         this.fixDisplay();
8053         this.beforeAction();
8054         this.storeXY(xy);
8055         var cb = this.createCB(c);
8056         supr.setXY.call(this, xy, a, d, cb, e);
8057         if(!a){
8058             cb();
8059         }
8060     },
8061
8062     // private
8063     createCB : function(c){
8064         var el = this;
8065         return function(){
8066             el.constrainXY();
8067             el.sync(true);
8068             if(c){
8069                 c();
8070             }
8071         };
8072     },
8073
8074     // overridden Element method
8075     setX : function(x, a, d, c, e){
8076         this.setXY([x, this.getY()], a, d, c, e);
8077     },
8078
8079     // overridden Element method
8080     setY : function(y, a, d, c, e){
8081         this.setXY([this.getX(), y], a, d, c, e);
8082     },
8083
8084     // overridden Element method
8085     setSize : function(w, h, a, d, c, e){
8086         this.beforeAction();
8087         var cb = this.createCB(c);
8088         supr.setSize.call(this, w, h, a, d, cb, e);
8089         if(!a){
8090             cb();
8091         }
8092     },
8093
8094     // overridden Element method
8095     setWidth : function(w, a, d, c, e){
8096         this.beforeAction();
8097         var cb = this.createCB(c);
8098         supr.setWidth.call(this, w, a, d, cb, e);
8099         if(!a){
8100             cb();
8101         }
8102     },
8103
8104     // overridden Element method
8105     setHeight : function(h, a, d, c, e){
8106         this.beforeAction();
8107         var cb = this.createCB(c);
8108         supr.setHeight.call(this, h, a, d, cb, e);
8109         if(!a){
8110             cb();
8111         }
8112     },
8113
8114     // overridden Element method
8115     setBounds : function(x, y, w, h, a, d, c, e){
8116         this.beforeAction();
8117         var cb = this.createCB(c);
8118         if(!a){
8119             this.storeXY([x, y]);
8120             supr.setXY.call(this, [x, y]);
8121             supr.setSize.call(this, w, h, a, d, cb, e);
8122             cb();
8123         }else{
8124             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8125         }
8126         return this;
8127     },
8128     
8129     /**
8130      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8131      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8132      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8133      * @param {Number} zindex The new z-index to set
8134      * @return {this} The Layer
8135      */
8136     setZIndex : function(zindex){
8137         this.zindex = zindex;
8138         this.setStyle("z-index", zindex + 2);
8139         if(this.shadow){
8140             this.shadow.setZIndex(zindex + 1);
8141         }
8142         if(this.shim){
8143             this.shim.setStyle("z-index", zindex);
8144         }
8145     }
8146 });
8147 })();/*
8148  * Based on:
8149  * Ext JS Library 1.1.1
8150  * Copyright(c) 2006-2007, Ext JS, LLC.
8151  *
8152  * Originally Released Under LGPL - original licence link has changed is not relivant.
8153  *
8154  * Fork - LGPL
8155  * <script type="text/javascript">
8156  */
8157
8158
8159 /**
8160  * @class Roo.Shadow
8161  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8162  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8163  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8164  * @constructor
8165  * Create a new Shadow
8166  * @param {Object} config The config object
8167  */
8168 Roo.Shadow = function(config){
8169     Roo.apply(this, config);
8170     if(typeof this.mode != "string"){
8171         this.mode = this.defaultMode;
8172     }
8173     var o = this.offset, a = {h: 0};
8174     var rad = Math.floor(this.offset/2);
8175     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8176         case "drop":
8177             a.w = 0;
8178             a.l = a.t = o;
8179             a.t -= 1;
8180             if(Roo.isIE){
8181                 a.l -= this.offset + rad;
8182                 a.t -= this.offset + rad;
8183                 a.w -= rad;
8184                 a.h -= rad;
8185                 a.t += 1;
8186             }
8187         break;
8188         case "sides":
8189             a.w = (o*2);
8190             a.l = -o;
8191             a.t = o-1;
8192             if(Roo.isIE){
8193                 a.l -= (this.offset - rad);
8194                 a.t -= this.offset + rad;
8195                 a.l += 1;
8196                 a.w -= (this.offset - rad)*2;
8197                 a.w -= rad + 1;
8198                 a.h -= 1;
8199             }
8200         break;
8201         case "frame":
8202             a.w = a.h = (o*2);
8203             a.l = a.t = -o;
8204             a.t += 1;
8205             a.h -= 2;
8206             if(Roo.isIE){
8207                 a.l -= (this.offset - rad);
8208                 a.t -= (this.offset - rad);
8209                 a.l += 1;
8210                 a.w -= (this.offset + rad + 1);
8211                 a.h -= (this.offset + rad);
8212                 a.h += 1;
8213             }
8214         break;
8215     };
8216
8217     this.adjusts = a;
8218 };
8219
8220 Roo.Shadow.prototype = {
8221     /**
8222      * @cfg {String} mode
8223      * The shadow display mode.  Supports the following options:<br />
8224      * sides: Shadow displays on both sides and bottom only<br />
8225      * frame: Shadow displays equally on all four sides<br />
8226      * drop: Traditional bottom-right drop shadow (default)
8227      */
8228     /**
8229      * @cfg {String} offset
8230      * The number of pixels to offset the shadow from the element (defaults to 4)
8231      */
8232     offset: 4,
8233
8234     // private
8235     defaultMode: "drop",
8236
8237     /**
8238      * Displays the shadow under the target element
8239      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8240      */
8241     show : function(target){
8242         target = Roo.get(target);
8243         if(!this.el){
8244             this.el = Roo.Shadow.Pool.pull();
8245             if(this.el.dom.nextSibling != target.dom){
8246                 this.el.insertBefore(target);
8247             }
8248         }
8249         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8250         if(Roo.isIE){
8251             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8252         }
8253         this.realign(
8254             target.getLeft(true),
8255             target.getTop(true),
8256             target.getWidth(),
8257             target.getHeight()
8258         );
8259         this.el.dom.style.display = "block";
8260     },
8261
8262     /**
8263      * Returns true if the shadow is visible, else false
8264      */
8265     isVisible : function(){
8266         return this.el ? true : false;  
8267     },
8268
8269     /**
8270      * Direct alignment when values are already available. Show must be called at least once before
8271      * calling this method to ensure it is initialized.
8272      * @param {Number} left The target element left position
8273      * @param {Number} top The target element top position
8274      * @param {Number} width The target element width
8275      * @param {Number} height The target element height
8276      */
8277     realign : function(l, t, w, h){
8278         if(!this.el){
8279             return;
8280         }
8281         var a = this.adjusts, d = this.el.dom, s = d.style;
8282         var iea = 0;
8283         s.left = (l+a.l)+"px";
8284         s.top = (t+a.t)+"px";
8285         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8286  
8287         if(s.width != sws || s.height != shs){
8288             s.width = sws;
8289             s.height = shs;
8290             if(!Roo.isIE){
8291                 var cn = d.childNodes;
8292                 var sww = Math.max(0, (sw-12))+"px";
8293                 cn[0].childNodes[1].style.width = sww;
8294                 cn[1].childNodes[1].style.width = sww;
8295                 cn[2].childNodes[1].style.width = sww;
8296                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8297             }
8298         }
8299     },
8300
8301     /**
8302      * Hides this shadow
8303      */
8304     hide : function(){
8305         if(this.el){
8306             this.el.dom.style.display = "none";
8307             Roo.Shadow.Pool.push(this.el);
8308             delete this.el;
8309         }
8310     },
8311
8312     /**
8313      * Adjust the z-index of this shadow
8314      * @param {Number} zindex The new z-index
8315      */
8316     setZIndex : function(z){
8317         this.zIndex = z;
8318         if(this.el){
8319             this.el.setStyle("z-index", z);
8320         }
8321     }
8322 };
8323
8324 // Private utility class that manages the internal Shadow cache
8325 Roo.Shadow.Pool = function(){
8326     var p = [];
8327     var markup = Roo.isIE ?
8328                  '<div class="x-ie-shadow"></div>' :
8329                  '<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>';
8330     return {
8331         pull : function(){
8332             var sh = p.shift();
8333             if(!sh){
8334                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8335                 sh.autoBoxAdjust = false;
8336             }
8337             return sh;
8338         },
8339
8340         push : function(sh){
8341             p.push(sh);
8342         }
8343     };
8344 }();/*
8345  * Based on:
8346  * Ext JS Library 1.1.1
8347  * Copyright(c) 2006-2007, Ext JS, LLC.
8348  *
8349  * Originally Released Under LGPL - original licence link has changed is not relivant.
8350  *
8351  * Fork - LGPL
8352  * <script type="text/javascript">
8353  */
8354
8355 /**
8356  * @class Roo.BoxComponent
8357  * @extends Roo.Component
8358  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8359  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8360  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8361  * layout containers.
8362  * @constructor
8363  * @param {Roo.Element/String/Object} config The configuration options.
8364  */
8365 Roo.BoxComponent = function(config){
8366     Roo.Component.call(this, config);
8367     this.addEvents({
8368         /**
8369          * @event resize
8370          * Fires after the component is resized.
8371              * @param {Roo.Component} this
8372              * @param {Number} adjWidth The box-adjusted width that was set
8373              * @param {Number} adjHeight The box-adjusted height that was set
8374              * @param {Number} rawWidth The width that was originally specified
8375              * @param {Number} rawHeight The height that was originally specified
8376              */
8377         resize : true,
8378         /**
8379          * @event move
8380          * Fires after the component is moved.
8381              * @param {Roo.Component} this
8382              * @param {Number} x The new x position
8383              * @param {Number} y The new y position
8384              */
8385         move : true
8386     });
8387 };
8388
8389 Roo.extend(Roo.BoxComponent, Roo.Component, {
8390     // private, set in afterRender to signify that the component has been rendered
8391     boxReady : false,
8392     // private, used to defer height settings to subclasses
8393     deferHeight: false,
8394     /** @cfg {Number} width
8395      * width (optional) size of component
8396      */
8397      /** @cfg {Number} height
8398      * height (optional) size of component
8399      */
8400      
8401     /**
8402      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8403      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8404      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8405      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8406      * @return {Roo.BoxComponent} this
8407      */
8408     setSize : function(w, h){
8409         // support for standard size objects
8410         if(typeof w == 'object'){
8411             h = w.height;
8412             w = w.width;
8413         }
8414         // not rendered
8415         if(!this.boxReady){
8416             this.width = w;
8417             this.height = h;
8418             return this;
8419         }
8420
8421         // prevent recalcs when not needed
8422         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8423             return this;
8424         }
8425         this.lastSize = {width: w, height: h};
8426
8427         var adj = this.adjustSize(w, h);
8428         var aw = adj.width, ah = adj.height;
8429         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8430             var rz = this.getResizeEl();
8431             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8432                 rz.setSize(aw, ah);
8433             }else if(!this.deferHeight && ah !== undefined){
8434                 rz.setHeight(ah);
8435             }else if(aw !== undefined){
8436                 rz.setWidth(aw);
8437             }
8438             this.onResize(aw, ah, w, h);
8439             this.fireEvent('resize', this, aw, ah, w, h);
8440         }
8441         return this;
8442     },
8443
8444     /**
8445      * Gets the current size of the component's underlying element.
8446      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8447      */
8448     getSize : function(){
8449         return this.el.getSize();
8450     },
8451
8452     /**
8453      * Gets the current XY position of the component's underlying element.
8454      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8455      * @return {Array} The XY position of the element (e.g., [100, 200])
8456      */
8457     getPosition : function(local){
8458         if(local === true){
8459             return [this.el.getLeft(true), this.el.getTop(true)];
8460         }
8461         return this.xy || this.el.getXY();
8462     },
8463
8464     /**
8465      * Gets the current box measurements of the component's underlying element.
8466      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8467      * @returns {Object} box An object in the format {x, y, width, height}
8468      */
8469     getBox : function(local){
8470         var s = this.el.getSize();
8471         if(local){
8472             s.x = this.el.getLeft(true);
8473             s.y = this.el.getTop(true);
8474         }else{
8475             var xy = this.xy || this.el.getXY();
8476             s.x = xy[0];
8477             s.y = xy[1];
8478         }
8479         return s;
8480     },
8481
8482     /**
8483      * Sets the current box measurements of the component's underlying element.
8484      * @param {Object} box An object in the format {x, y, width, height}
8485      * @returns {Roo.BoxComponent} this
8486      */
8487     updateBox : function(box){
8488         this.setSize(box.width, box.height);
8489         this.setPagePosition(box.x, box.y);
8490         return this;
8491     },
8492
8493     // protected
8494     getResizeEl : function(){
8495         return this.resizeEl || this.el;
8496     },
8497
8498     // protected
8499     getPositionEl : function(){
8500         return this.positionEl || this.el;
8501     },
8502
8503     /**
8504      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8505      * This method fires the move event.
8506      * @param {Number} left The new left
8507      * @param {Number} top The new top
8508      * @returns {Roo.BoxComponent} this
8509      */
8510     setPosition : function(x, y){
8511         this.x = x;
8512         this.y = y;
8513         if(!this.boxReady){
8514             return this;
8515         }
8516         var adj = this.adjustPosition(x, y);
8517         var ax = adj.x, ay = adj.y;
8518
8519         var el = this.getPositionEl();
8520         if(ax !== undefined || ay !== undefined){
8521             if(ax !== undefined && ay !== undefined){
8522                 el.setLeftTop(ax, ay);
8523             }else if(ax !== undefined){
8524                 el.setLeft(ax);
8525             }else if(ay !== undefined){
8526                 el.setTop(ay);
8527             }
8528             this.onPosition(ax, ay);
8529             this.fireEvent('move', this, ax, ay);
8530         }
8531         return this;
8532     },
8533
8534     /**
8535      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8536      * This method fires the move event.
8537      * @param {Number} x The new x position
8538      * @param {Number} y The new y position
8539      * @returns {Roo.BoxComponent} this
8540      */
8541     setPagePosition : function(x, y){
8542         this.pageX = x;
8543         this.pageY = y;
8544         if(!this.boxReady){
8545             return;
8546         }
8547         if(x === undefined || y === undefined){ // cannot translate undefined points
8548             return;
8549         }
8550         var p = this.el.translatePoints(x, y);
8551         this.setPosition(p.left, p.top);
8552         return this;
8553     },
8554
8555     // private
8556     onRender : function(ct, position){
8557         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8558         if(this.resizeEl){
8559             this.resizeEl = Roo.get(this.resizeEl);
8560         }
8561         if(this.positionEl){
8562             this.positionEl = Roo.get(this.positionEl);
8563         }
8564     },
8565
8566     // private
8567     afterRender : function(){
8568         Roo.BoxComponent.superclass.afterRender.call(this);
8569         this.boxReady = true;
8570         this.setSize(this.width, this.height);
8571         if(this.x || this.y){
8572             this.setPosition(this.x, this.y);
8573         }
8574         if(this.pageX || this.pageY){
8575             this.setPagePosition(this.pageX, this.pageY);
8576         }
8577     },
8578
8579     /**
8580      * Force the component's size to recalculate based on the underlying element's current height and width.
8581      * @returns {Roo.BoxComponent} this
8582      */
8583     syncSize : function(){
8584         delete this.lastSize;
8585         this.setSize(this.el.getWidth(), this.el.getHeight());
8586         return this;
8587     },
8588
8589     /**
8590      * Called after the component is resized, this method is empty by default but can be implemented by any
8591      * subclass that needs to perform custom logic after a resize occurs.
8592      * @param {Number} adjWidth The box-adjusted width that was set
8593      * @param {Number} adjHeight The box-adjusted height that was set
8594      * @param {Number} rawWidth The width that was originally specified
8595      * @param {Number} rawHeight The height that was originally specified
8596      */
8597     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8598
8599     },
8600
8601     /**
8602      * Called after the component is moved, this method is empty by default but can be implemented by any
8603      * subclass that needs to perform custom logic after a move occurs.
8604      * @param {Number} x The new x position
8605      * @param {Number} y The new y position
8606      */
8607     onPosition : function(x, y){
8608
8609     },
8610
8611     // private
8612     adjustSize : function(w, h){
8613         if(this.autoWidth){
8614             w = 'auto';
8615         }
8616         if(this.autoHeight){
8617             h = 'auto';
8618         }
8619         return {width : w, height: h};
8620     },
8621
8622     // private
8623     adjustPosition : function(x, y){
8624         return {x : x, y: y};
8625     }
8626 });/*
8627  * Based on:
8628  * Ext JS Library 1.1.1
8629  * Copyright(c) 2006-2007, Ext JS, LLC.
8630  *
8631  * Originally Released Under LGPL - original licence link has changed is not relivant.
8632  *
8633  * Fork - LGPL
8634  * <script type="text/javascript">
8635  */
8636
8637
8638 /**
8639  * @class Roo.SplitBar
8640  * @extends Roo.util.Observable
8641  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8642  * <br><br>
8643  * Usage:
8644  * <pre><code>
8645 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8646                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8647 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8648 split.minSize = 100;
8649 split.maxSize = 600;
8650 split.animate = true;
8651 split.on('moved', splitterMoved);
8652 </code></pre>
8653  * @constructor
8654  * Create a new SplitBar
8655  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8656  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8657  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8658  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8659                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8660                         position of the SplitBar).
8661  */
8662 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8663     
8664     /** @private */
8665     this.el = Roo.get(dragElement, true);
8666     this.el.dom.unselectable = "on";
8667     /** @private */
8668     this.resizingEl = Roo.get(resizingElement, true);
8669
8670     /**
8671      * @private
8672      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8673      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8674      * @type Number
8675      */
8676     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8677     
8678     /**
8679      * The minimum size of the resizing element. (Defaults to 0)
8680      * @type Number
8681      */
8682     this.minSize = 0;
8683     
8684     /**
8685      * The maximum size of the resizing element. (Defaults to 2000)
8686      * @type Number
8687      */
8688     this.maxSize = 2000;
8689     
8690     /**
8691      * Whether to animate the transition to the new size
8692      * @type Boolean
8693      */
8694     this.animate = false;
8695     
8696     /**
8697      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8698      * @type Boolean
8699      */
8700     this.useShim = false;
8701     
8702     /** @private */
8703     this.shim = null;
8704     
8705     if(!existingProxy){
8706         /** @private */
8707         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8708     }else{
8709         this.proxy = Roo.get(existingProxy).dom;
8710     }
8711     /** @private */
8712     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8713     
8714     /** @private */
8715     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8716     
8717     /** @private */
8718     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8719     
8720     /** @private */
8721     this.dragSpecs = {};
8722     
8723     /**
8724      * @private The adapter to use to positon and resize elements
8725      */
8726     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8727     this.adapter.init(this);
8728     
8729     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8730         /** @private */
8731         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8732         this.el.addClass("x-splitbar-h");
8733     }else{
8734         /** @private */
8735         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8736         this.el.addClass("x-splitbar-v");
8737     }
8738     
8739     this.addEvents({
8740         /**
8741          * @event resize
8742          * Fires when the splitter is moved (alias for {@link #event-moved})
8743          * @param {Roo.SplitBar} this
8744          * @param {Number} newSize the new width or height
8745          */
8746         "resize" : true,
8747         /**
8748          * @event moved
8749          * Fires when the splitter is moved
8750          * @param {Roo.SplitBar} this
8751          * @param {Number} newSize the new width or height
8752          */
8753         "moved" : true,
8754         /**
8755          * @event beforeresize
8756          * Fires before the splitter is dragged
8757          * @param {Roo.SplitBar} this
8758          */
8759         "beforeresize" : true,
8760
8761         "beforeapply" : true
8762     });
8763
8764     Roo.util.Observable.call(this);
8765 };
8766
8767 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8768     onStartProxyDrag : function(x, y){
8769         this.fireEvent("beforeresize", this);
8770         if(!this.overlay){
8771             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8772             o.unselectable();
8773             o.enableDisplayMode("block");
8774             // all splitbars share the same overlay
8775             Roo.SplitBar.prototype.overlay = o;
8776         }
8777         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8778         this.overlay.show();
8779         Roo.get(this.proxy).setDisplayed("block");
8780         var size = this.adapter.getElementSize(this);
8781         this.activeMinSize = this.getMinimumSize();;
8782         this.activeMaxSize = this.getMaximumSize();;
8783         var c1 = size - this.activeMinSize;
8784         var c2 = Math.max(this.activeMaxSize - size, 0);
8785         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8786             this.dd.resetConstraints();
8787             this.dd.setXConstraint(
8788                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8789                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8790             );
8791             this.dd.setYConstraint(0, 0);
8792         }else{
8793             this.dd.resetConstraints();
8794             this.dd.setXConstraint(0, 0);
8795             this.dd.setYConstraint(
8796                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8797                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8798             );
8799          }
8800         this.dragSpecs.startSize = size;
8801         this.dragSpecs.startPoint = [x, y];
8802         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8803     },
8804     
8805     /** 
8806      * @private Called after the drag operation by the DDProxy
8807      */
8808     onEndProxyDrag : function(e){
8809         Roo.get(this.proxy).setDisplayed(false);
8810         var endPoint = Roo.lib.Event.getXY(e);
8811         if(this.overlay){
8812             this.overlay.hide();
8813         }
8814         var newSize;
8815         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8816             newSize = this.dragSpecs.startSize + 
8817                 (this.placement == Roo.SplitBar.LEFT ?
8818                     endPoint[0] - this.dragSpecs.startPoint[0] :
8819                     this.dragSpecs.startPoint[0] - endPoint[0]
8820                 );
8821         }else{
8822             newSize = this.dragSpecs.startSize + 
8823                 (this.placement == Roo.SplitBar.TOP ?
8824                     endPoint[1] - this.dragSpecs.startPoint[1] :
8825                     this.dragSpecs.startPoint[1] - endPoint[1]
8826                 );
8827         }
8828         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8829         if(newSize != this.dragSpecs.startSize){
8830             if(this.fireEvent('beforeapply', this, newSize) !== false){
8831                 this.adapter.setElementSize(this, newSize);
8832                 this.fireEvent("moved", this, newSize);
8833                 this.fireEvent("resize", this, newSize);
8834             }
8835         }
8836     },
8837     
8838     /**
8839      * Get the adapter this SplitBar uses
8840      * @return The adapter object
8841      */
8842     getAdapter : function(){
8843         return this.adapter;
8844     },
8845     
8846     /**
8847      * Set the adapter this SplitBar uses
8848      * @param {Object} adapter A SplitBar adapter object
8849      */
8850     setAdapter : function(adapter){
8851         this.adapter = adapter;
8852         this.adapter.init(this);
8853     },
8854     
8855     /**
8856      * Gets the minimum size for the resizing element
8857      * @return {Number} The minimum size
8858      */
8859     getMinimumSize : function(){
8860         return this.minSize;
8861     },
8862     
8863     /**
8864      * Sets the minimum size for the resizing element
8865      * @param {Number} minSize The minimum size
8866      */
8867     setMinimumSize : function(minSize){
8868         this.minSize = minSize;
8869     },
8870     
8871     /**
8872      * Gets the maximum size for the resizing element
8873      * @return {Number} The maximum size
8874      */
8875     getMaximumSize : function(){
8876         return this.maxSize;
8877     },
8878     
8879     /**
8880      * Sets the maximum size for the resizing element
8881      * @param {Number} maxSize The maximum size
8882      */
8883     setMaximumSize : function(maxSize){
8884         this.maxSize = maxSize;
8885     },
8886     
8887     /**
8888      * Sets the initialize size for the resizing element
8889      * @param {Number} size The initial size
8890      */
8891     setCurrentSize : function(size){
8892         var oldAnimate = this.animate;
8893         this.animate = false;
8894         this.adapter.setElementSize(this, size);
8895         this.animate = oldAnimate;
8896     },
8897     
8898     /**
8899      * Destroy this splitbar. 
8900      * @param {Boolean} removeEl True to remove the element
8901      */
8902     destroy : function(removeEl){
8903         if(this.shim){
8904             this.shim.remove();
8905         }
8906         this.dd.unreg();
8907         this.proxy.parentNode.removeChild(this.proxy);
8908         if(removeEl){
8909             this.el.remove();
8910         }
8911     }
8912 });
8913
8914 /**
8915  * @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.
8916  */
8917 Roo.SplitBar.createProxy = function(dir){
8918     var proxy = new Roo.Element(document.createElement("div"));
8919     proxy.unselectable();
8920     var cls = 'x-splitbar-proxy';
8921     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8922     document.body.appendChild(proxy.dom);
8923     return proxy.dom;
8924 };
8925
8926 /** 
8927  * @class Roo.SplitBar.BasicLayoutAdapter
8928  * Default Adapter. It assumes the splitter and resizing element are not positioned
8929  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8930  */
8931 Roo.SplitBar.BasicLayoutAdapter = function(){
8932 };
8933
8934 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8935     // do nothing for now
8936     init : function(s){
8937     
8938     },
8939     /**
8940      * Called before drag operations to get the current size of the resizing element. 
8941      * @param {Roo.SplitBar} s The SplitBar using this adapter
8942      */
8943      getElementSize : function(s){
8944         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8945             return s.resizingEl.getWidth();
8946         }else{
8947             return s.resizingEl.getHeight();
8948         }
8949     },
8950     
8951     /**
8952      * Called after drag operations to set the size of the resizing element.
8953      * @param {Roo.SplitBar} s The SplitBar using this adapter
8954      * @param {Number} newSize The new size to set
8955      * @param {Function} onComplete A function to be invoked when resizing is complete
8956      */
8957     setElementSize : function(s, newSize, onComplete){
8958         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8959             if(!s.animate){
8960                 s.resizingEl.setWidth(newSize);
8961                 if(onComplete){
8962                     onComplete(s, newSize);
8963                 }
8964             }else{
8965                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8966             }
8967         }else{
8968             
8969             if(!s.animate){
8970                 s.resizingEl.setHeight(newSize);
8971                 if(onComplete){
8972                     onComplete(s, newSize);
8973                 }
8974             }else{
8975                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8976             }
8977         }
8978     }
8979 };
8980
8981 /** 
8982  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8983  * @extends Roo.SplitBar.BasicLayoutAdapter
8984  * Adapter that  moves the splitter element to align with the resized sizing element. 
8985  * Used with an absolute positioned SplitBar.
8986  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8987  * document.body, make sure you assign an id to the body element.
8988  */
8989 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8990     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8991     this.container = Roo.get(container);
8992 };
8993
8994 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8995     init : function(s){
8996         this.basic.init(s);
8997     },
8998     
8999     getElementSize : function(s){
9000         return this.basic.getElementSize(s);
9001     },
9002     
9003     setElementSize : function(s, newSize, onComplete){
9004         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9005     },
9006     
9007     moveSplitter : function(s){
9008         var yes = Roo.SplitBar;
9009         switch(s.placement){
9010             case yes.LEFT:
9011                 s.el.setX(s.resizingEl.getRight());
9012                 break;
9013             case yes.RIGHT:
9014                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9015                 break;
9016             case yes.TOP:
9017                 s.el.setY(s.resizingEl.getBottom());
9018                 break;
9019             case yes.BOTTOM:
9020                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9021                 break;
9022         }
9023     }
9024 };
9025
9026 /**
9027  * Orientation constant - Create a vertical SplitBar
9028  * @static
9029  * @type Number
9030  */
9031 Roo.SplitBar.VERTICAL = 1;
9032
9033 /**
9034  * Orientation constant - Create a horizontal SplitBar
9035  * @static
9036  * @type Number
9037  */
9038 Roo.SplitBar.HORIZONTAL = 2;
9039
9040 /**
9041  * Placement constant - The resizing element is to the left of the splitter element
9042  * @static
9043  * @type Number
9044  */
9045 Roo.SplitBar.LEFT = 1;
9046
9047 /**
9048  * Placement constant - The resizing element is to the right of the splitter element
9049  * @static
9050  * @type Number
9051  */
9052 Roo.SplitBar.RIGHT = 2;
9053
9054 /**
9055  * Placement constant - The resizing element is positioned above the splitter element
9056  * @static
9057  * @type Number
9058  */
9059 Roo.SplitBar.TOP = 3;
9060
9061 /**
9062  * Placement constant - The resizing element is positioned under splitter element
9063  * @static
9064  * @type Number
9065  */
9066 Roo.SplitBar.BOTTOM = 4;
9067 /*
9068  * Based on:
9069  * Ext JS Library 1.1.1
9070  * Copyright(c) 2006-2007, Ext JS, LLC.
9071  *
9072  * Originally Released Under LGPL - original licence link has changed is not relivant.
9073  *
9074  * Fork - LGPL
9075  * <script type="text/javascript">
9076  */
9077
9078 /**
9079  * @class Roo.View
9080  * @extends Roo.util.Observable
9081  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9082  * This class also supports single and multi selection modes. <br>
9083  * Create a data model bound view:
9084  <pre><code>
9085  var store = new Roo.data.Store(...);
9086
9087  var view = new Roo.View({
9088     el : "my-element",
9089     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9090  
9091     singleSelect: true,
9092     selectedClass: "ydataview-selected",
9093     store: store
9094  });
9095
9096  // listen for node click?
9097  view.on("click", function(vw, index, node, e){
9098  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9099  });
9100
9101  // load XML data
9102  dataModel.load("foobar.xml");
9103  </code></pre>
9104  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9105  * <br><br>
9106  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9107  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9108  * 
9109  * Note: old style constructor is still suported (container, template, config)
9110  * 
9111  * @constructor
9112  * Create a new View
9113  * @param {Object} config The config object
9114  * 
9115  */
9116 Roo.View = function(config, depreciated_tpl, depreciated_config){
9117     
9118     if (typeof(depreciated_tpl) == 'undefined') {
9119         // new way.. - universal constructor.
9120         Roo.apply(this, config);
9121         this.el  = Roo.get(this.el);
9122     } else {
9123         // old format..
9124         this.el  = Roo.get(config);
9125         this.tpl = depreciated_tpl;
9126         Roo.apply(this, depreciated_config);
9127     }
9128      
9129     
9130     if(typeof(this.tpl) == "string"){
9131         this.tpl = new Roo.Template(this.tpl);
9132     } else {
9133         // support xtype ctors..
9134         this.tpl = new Roo.factory(this.tpl, Roo);
9135     }
9136     
9137     
9138     this.tpl.compile();
9139    
9140
9141      
9142     /** @private */
9143     this.addEvents({
9144     /**
9145      * @event beforeclick
9146      * Fires before a click is processed. Returns false to cancel the default action.
9147      * @param {Roo.View} this
9148      * @param {Number} index The index of the target node
9149      * @param {HTMLElement} node The target node
9150      * @param {Roo.EventObject} e The raw event object
9151      */
9152         "beforeclick" : true,
9153     /**
9154      * @event click
9155      * Fires when a template node is clicked.
9156      * @param {Roo.View} this
9157      * @param {Number} index The index of the target node
9158      * @param {HTMLElement} node The target node
9159      * @param {Roo.EventObject} e The raw event object
9160      */
9161         "click" : true,
9162     /**
9163      * @event dblclick
9164      * Fires when a template node is double clicked.
9165      * @param {Roo.View} this
9166      * @param {Number} index The index of the target node
9167      * @param {HTMLElement} node The target node
9168      * @param {Roo.EventObject} e The raw event object
9169      */
9170         "dblclick" : true,
9171     /**
9172      * @event contextmenu
9173      * Fires when a template node is right clicked.
9174      * @param {Roo.View} this
9175      * @param {Number} index The index of the target node
9176      * @param {HTMLElement} node The target node
9177      * @param {Roo.EventObject} e The raw event object
9178      */
9179         "contextmenu" : true,
9180     /**
9181      * @event selectionchange
9182      * Fires when the selected nodes change.
9183      * @param {Roo.View} this
9184      * @param {Array} selections Array of the selected nodes
9185      */
9186         "selectionchange" : true,
9187
9188     /**
9189      * @event beforeselect
9190      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9191      * @param {Roo.View} this
9192      * @param {HTMLElement} node The node to be selected
9193      * @param {Array} selections Array of currently selected nodes
9194      */
9195         "beforeselect" : true
9196     });
9197
9198     this.el.on({
9199         "click": this.onClick,
9200         "dblclick": this.onDblClick,
9201         "contextmenu": this.onContextMenu,
9202         scope:this
9203     });
9204
9205     this.selections = [];
9206     this.nodes = [];
9207     this.cmp = new Roo.CompositeElementLite([]);
9208     if(this.store){
9209         this.store = Roo.factory(this.store, Roo.data);
9210         this.setStore(this.store, true);
9211     }
9212     Roo.View.superclass.constructor.call(this);
9213 };
9214
9215 Roo.extend(Roo.View, Roo.util.Observable, {
9216     
9217      /**
9218      * @cfg {Roo.data.Store} store Data store to load data from.
9219      */
9220     store : false,
9221     
9222     /**
9223      * @cfg {String|Roo.Element} el The container element.
9224      */
9225     el : '',
9226     
9227     /**
9228      * @cfg {String|Roo.Template} tpl The template used by this View 
9229      */
9230     tpl : false,
9231     
9232     /**
9233      * @cfg {String} selectedClass The css class to add to selected nodes
9234      */
9235     selectedClass : "x-view-selected",
9236      /**
9237      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9238      */
9239     emptyText : "",
9240     /**
9241      * @cfg {Boolean} multiSelect Allow multiple selection
9242      */
9243     
9244     multiSelect : false,
9245     /**
9246      * @cfg {Boolean} singleSelect Allow single selection
9247      */
9248     singleSelect:  false,
9249     
9250     /**
9251      * Returns the element this view is bound to.
9252      * @return {Roo.Element}
9253      */
9254     getEl : function(){
9255         return this.el;
9256     },
9257
9258     /**
9259      * Refreshes the view.
9260      */
9261     refresh : function(){
9262         var t = this.tpl;
9263         this.clearSelections();
9264         this.el.update("");
9265         var html = [];
9266         var records = this.store.getRange();
9267         if(records.length < 1){
9268             this.el.update(this.emptyText);
9269             return;
9270         }
9271         for(var i = 0, len = records.length; i < len; i++){
9272             var data = this.prepareData(records[i].data, i, records[i]);
9273             html[html.length] = t.apply(data);
9274         }
9275         this.el.update(html.join(""));
9276         this.nodes = this.el.dom.childNodes;
9277         this.updateIndexes(0);
9278     },
9279
9280     /**
9281      * Function to override to reformat the data that is sent to
9282      * the template for each node.
9283      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9284      * a JSON object for an UpdateManager bound view).
9285      */
9286     prepareData : function(data){
9287         return data;
9288     },
9289
9290     onUpdate : function(ds, record){
9291         this.clearSelections();
9292         var index = this.store.indexOf(record);
9293         var n = this.nodes[index];
9294         this.tpl.insertBefore(n, this.prepareData(record.data));
9295         n.parentNode.removeChild(n);
9296         this.updateIndexes(index, index);
9297     },
9298
9299     onAdd : function(ds, records, index){
9300         this.clearSelections();
9301         if(this.nodes.length == 0){
9302             this.refresh();
9303             return;
9304         }
9305         var n = this.nodes[index];
9306         for(var i = 0, len = records.length; i < len; i++){
9307             var d = this.prepareData(records[i].data);
9308             if(n){
9309                 this.tpl.insertBefore(n, d);
9310             }else{
9311                 this.tpl.append(this.el, d);
9312             }
9313         }
9314         this.updateIndexes(index);
9315     },
9316
9317     onRemove : function(ds, record, index){
9318         this.clearSelections();
9319         this.el.dom.removeChild(this.nodes[index]);
9320         this.updateIndexes(index);
9321     },
9322
9323     /**
9324      * Refresh an individual node.
9325      * @param {Number} index
9326      */
9327     refreshNode : function(index){
9328         this.onUpdate(this.store, this.store.getAt(index));
9329     },
9330
9331     updateIndexes : function(startIndex, endIndex){
9332         var ns = this.nodes;
9333         startIndex = startIndex || 0;
9334         endIndex = endIndex || ns.length - 1;
9335         for(var i = startIndex; i <= endIndex; i++){
9336             ns[i].nodeIndex = i;
9337         }
9338     },
9339
9340     /**
9341      * Changes the data store this view uses and refresh the view.
9342      * @param {Store} store
9343      */
9344     setStore : function(store, initial){
9345         if(!initial && this.store){
9346             this.store.un("datachanged", this.refresh);
9347             this.store.un("add", this.onAdd);
9348             this.store.un("remove", this.onRemove);
9349             this.store.un("update", this.onUpdate);
9350             this.store.un("clear", this.refresh);
9351         }
9352         if(store){
9353           
9354             store.on("datachanged", this.refresh, this);
9355             store.on("add", this.onAdd, this);
9356             store.on("remove", this.onRemove, this);
9357             store.on("update", this.onUpdate, this);
9358             store.on("clear", this.refresh, this);
9359         }
9360         
9361         if(store){
9362             this.refresh();
9363         }
9364     },
9365
9366     /**
9367      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9368      * @param {HTMLElement} node
9369      * @return {HTMLElement} The template node
9370      */
9371     findItemFromChild : function(node){
9372         var el = this.el.dom;
9373         if(!node || node.parentNode == el){
9374                     return node;
9375             }
9376             var p = node.parentNode;
9377             while(p && p != el){
9378             if(p.parentNode == el){
9379                 return p;
9380             }
9381             p = p.parentNode;
9382         }
9383             return null;
9384     },
9385
9386     /** @ignore */
9387     onClick : function(e){
9388         var item = this.findItemFromChild(e.getTarget());
9389         if(item){
9390             var index = this.indexOf(item);
9391             if(this.onItemClick(item, index, e) !== false){
9392                 this.fireEvent("click", this, index, item, e);
9393             }
9394         }else{
9395             this.clearSelections();
9396         }
9397     },
9398
9399     /** @ignore */
9400     onContextMenu : function(e){
9401         var item = this.findItemFromChild(e.getTarget());
9402         if(item){
9403             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9404         }
9405     },
9406
9407     /** @ignore */
9408     onDblClick : function(e){
9409         var item = this.findItemFromChild(e.getTarget());
9410         if(item){
9411             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9412         }
9413     },
9414
9415     onItemClick : function(item, index, e){
9416         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9417             return false;
9418         }
9419         if(this.multiSelect || this.singleSelect){
9420             if(this.multiSelect && e.shiftKey && this.lastSelection){
9421                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9422             }else{
9423                 this.select(item, this.multiSelect && e.ctrlKey);
9424                 this.lastSelection = item;
9425             }
9426             e.preventDefault();
9427         }
9428         return true;
9429     },
9430
9431     /**
9432      * Get the number of selected nodes.
9433      * @return {Number}
9434      */
9435     getSelectionCount : function(){
9436         return this.selections.length;
9437     },
9438
9439     /**
9440      * Get the currently selected nodes.
9441      * @return {Array} An array of HTMLElements
9442      */
9443     getSelectedNodes : function(){
9444         return this.selections;
9445     },
9446
9447     /**
9448      * Get the indexes of the selected nodes.
9449      * @return {Array}
9450      */
9451     getSelectedIndexes : function(){
9452         var indexes = [], s = this.selections;
9453         for(var i = 0, len = s.length; i < len; i++){
9454             indexes.push(s[i].nodeIndex);
9455         }
9456         return indexes;
9457     },
9458
9459     /**
9460      * Clear all selections
9461      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9462      */
9463     clearSelections : function(suppressEvent){
9464         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9465             this.cmp.elements = this.selections;
9466             this.cmp.removeClass(this.selectedClass);
9467             this.selections = [];
9468             if(!suppressEvent){
9469                 this.fireEvent("selectionchange", this, this.selections);
9470             }
9471         }
9472     },
9473
9474     /**
9475      * Returns true if the passed node is selected
9476      * @param {HTMLElement/Number} node The node or node index
9477      * @return {Boolean}
9478      */
9479     isSelected : function(node){
9480         var s = this.selections;
9481         if(s.length < 1){
9482             return false;
9483         }
9484         node = this.getNode(node);
9485         return s.indexOf(node) !== -1;
9486     },
9487
9488     /**
9489      * Selects nodes.
9490      * @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
9491      * @param {Boolean} keepExisting (optional) true to keep existing selections
9492      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9493      */
9494     select : function(nodeInfo, keepExisting, suppressEvent){
9495         if(nodeInfo instanceof Array){
9496             if(!keepExisting){
9497                 this.clearSelections(true);
9498             }
9499             for(var i = 0, len = nodeInfo.length; i < len; i++){
9500                 this.select(nodeInfo[i], true, true);
9501             }
9502         } else{
9503             var node = this.getNode(nodeInfo);
9504             if(node && !this.isSelected(node)){
9505                 if(!keepExisting){
9506                     this.clearSelections(true);
9507                 }
9508                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9509                     Roo.fly(node).addClass(this.selectedClass);
9510                     this.selections.push(node);
9511                     if(!suppressEvent){
9512                         this.fireEvent("selectionchange", this, this.selections);
9513                     }
9514                 }
9515             }
9516         }
9517     },
9518
9519     /**
9520      * Gets a template node.
9521      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9522      * @return {HTMLElement} The node or null if it wasn't found
9523      */
9524     getNode : function(nodeInfo){
9525         if(typeof nodeInfo == "string"){
9526             return document.getElementById(nodeInfo);
9527         }else if(typeof nodeInfo == "number"){
9528             return this.nodes[nodeInfo];
9529         }
9530         return nodeInfo;
9531     },
9532
9533     /**
9534      * Gets a range template nodes.
9535      * @param {Number} startIndex
9536      * @param {Number} endIndex
9537      * @return {Array} An array of nodes
9538      */
9539     getNodes : function(start, end){
9540         var ns = this.nodes;
9541         start = start || 0;
9542         end = typeof end == "undefined" ? ns.length - 1 : end;
9543         var nodes = [];
9544         if(start <= end){
9545             for(var i = start; i <= end; i++){
9546                 nodes.push(ns[i]);
9547             }
9548         } else{
9549             for(var i = start; i >= end; i--){
9550                 nodes.push(ns[i]);
9551             }
9552         }
9553         return nodes;
9554     },
9555
9556     /**
9557      * Finds the index of the passed node
9558      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9559      * @return {Number} The index of the node or -1
9560      */
9561     indexOf : function(node){
9562         node = this.getNode(node);
9563         if(typeof node.nodeIndex == "number"){
9564             return node.nodeIndex;
9565         }
9566         var ns = this.nodes;
9567         for(var i = 0, len = ns.length; i < len; i++){
9568             if(ns[i] == node){
9569                 return i;
9570             }
9571         }
9572         return -1;
9573     }
9574 });
9575 /*
9576  * Based on:
9577  * Ext JS Library 1.1.1
9578  * Copyright(c) 2006-2007, Ext JS, LLC.
9579  *
9580  * Originally Released Under LGPL - original licence link has changed is not relivant.
9581  *
9582  * Fork - LGPL
9583  * <script type="text/javascript">
9584  */
9585
9586 /**
9587  * @class Roo.JsonView
9588  * @extends Roo.View
9589  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9590 <pre><code>
9591 var view = new Roo.JsonView({
9592     container: "my-element",
9593     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9594     multiSelect: true, 
9595     jsonRoot: "data" 
9596 });
9597
9598 // listen for node click?
9599 view.on("click", function(vw, index, node, e){
9600     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9601 });
9602
9603 // direct load of JSON data
9604 view.load("foobar.php");
9605
9606 // Example from my blog list
9607 var tpl = new Roo.Template(
9608     '&lt;div class="entry"&gt;' +
9609     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9610     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9611     "&lt;/div&gt;&lt;hr /&gt;"
9612 );
9613
9614 var moreView = new Roo.JsonView({
9615     container :  "entry-list", 
9616     template : tpl,
9617     jsonRoot: "posts"
9618 });
9619 moreView.on("beforerender", this.sortEntries, this);
9620 moreView.load({
9621     url: "/blog/get-posts.php",
9622     params: "allposts=true",
9623     text: "Loading Blog Entries..."
9624 });
9625 </code></pre>
9626
9627 * Note: old code is supported with arguments : (container, template, config)
9628
9629
9630  * @constructor
9631  * Create a new JsonView
9632  * 
9633  * @param {Object} config The config object
9634  * 
9635  */
9636 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9637     
9638     
9639     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9640
9641     var um = this.el.getUpdateManager();
9642     um.setRenderer(this);
9643     um.on("update", this.onLoad, this);
9644     um.on("failure", this.onLoadException, this);
9645
9646     /**
9647      * @event beforerender
9648      * Fires before rendering of the downloaded JSON data.
9649      * @param {Roo.JsonView} this
9650      * @param {Object} data The JSON data loaded
9651      */
9652     /**
9653      * @event load
9654      * Fires when data is loaded.
9655      * @param {Roo.JsonView} this
9656      * @param {Object} data The JSON data loaded
9657      * @param {Object} response The raw Connect response object
9658      */
9659     /**
9660      * @event loadexception
9661      * Fires when loading fails.
9662      * @param {Roo.JsonView} this
9663      * @param {Object} response The raw Connect response object
9664      */
9665     this.addEvents({
9666         'beforerender' : true,
9667         'load' : true,
9668         'loadexception' : true
9669     });
9670 };
9671 Roo.extend(Roo.JsonView, Roo.View, {
9672     /**
9673      * @type {String} The root property in the loaded JSON object that contains the data
9674      */
9675     jsonRoot : "",
9676
9677     /**
9678      * Refreshes the view.
9679      */
9680     refresh : function(){
9681         this.clearSelections();
9682         this.el.update("");
9683         var html = [];
9684         var o = this.jsonData;
9685         if(o && o.length > 0){
9686             for(var i = 0, len = o.length; i < len; i++){
9687                 var data = this.prepareData(o[i], i, o);
9688                 html[html.length] = this.tpl.apply(data);
9689             }
9690         }else{
9691             html.push(this.emptyText);
9692         }
9693         this.el.update(html.join(""));
9694         this.nodes = this.el.dom.childNodes;
9695         this.updateIndexes(0);
9696     },
9697
9698     /**
9699      * 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.
9700      * @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:
9701      <pre><code>
9702      view.load({
9703          url: "your-url.php",
9704          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9705          callback: yourFunction,
9706          scope: yourObject, //(optional scope)
9707          discardUrl: false,
9708          nocache: false,
9709          text: "Loading...",
9710          timeout: 30,
9711          scripts: false
9712      });
9713      </code></pre>
9714      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9715      * 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.
9716      * @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}
9717      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9718      * @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.
9719      */
9720     load : function(){
9721         var um = this.el.getUpdateManager();
9722         um.update.apply(um, arguments);
9723     },
9724
9725     render : function(el, response){
9726         this.clearSelections();
9727         this.el.update("");
9728         var o;
9729         try{
9730             o = Roo.util.JSON.decode(response.responseText);
9731             if(this.jsonRoot){
9732                 
9733                 o = o[this.jsonRoot];
9734             }
9735         } catch(e){
9736         }
9737         /**
9738          * The current JSON data or null
9739          */
9740         this.jsonData = o;
9741         this.beforeRender();
9742         this.refresh();
9743     },
9744
9745 /**
9746  * Get the number of records in the current JSON dataset
9747  * @return {Number}
9748  */
9749     getCount : function(){
9750         return this.jsonData ? this.jsonData.length : 0;
9751     },
9752
9753 /**
9754  * Returns the JSON object for the specified node(s)
9755  * @param {HTMLElement/Array} node The node or an array of nodes
9756  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9757  * you get the JSON object for the node
9758  */
9759     getNodeData : function(node){
9760         if(node instanceof Array){
9761             var data = [];
9762             for(var i = 0, len = node.length; i < len; i++){
9763                 data.push(this.getNodeData(node[i]));
9764             }
9765             return data;
9766         }
9767         return this.jsonData[this.indexOf(node)] || null;
9768     },
9769
9770     beforeRender : function(){
9771         this.snapshot = this.jsonData;
9772         if(this.sortInfo){
9773             this.sort.apply(this, this.sortInfo);
9774         }
9775         this.fireEvent("beforerender", this, this.jsonData);
9776     },
9777
9778     onLoad : function(el, o){
9779         this.fireEvent("load", this, this.jsonData, o);
9780     },
9781
9782     onLoadException : function(el, o){
9783         this.fireEvent("loadexception", this, o);
9784     },
9785
9786 /**
9787  * Filter the data by a specific property.
9788  * @param {String} property A property on your JSON objects
9789  * @param {String/RegExp} value Either string that the property values
9790  * should start with, or a RegExp to test against the property
9791  */
9792     filter : function(property, value){
9793         if(this.jsonData){
9794             var data = [];
9795             var ss = this.snapshot;
9796             if(typeof value == "string"){
9797                 var vlen = value.length;
9798                 if(vlen == 0){
9799                     this.clearFilter();
9800                     return;
9801                 }
9802                 value = value.toLowerCase();
9803                 for(var i = 0, len = ss.length; i < len; i++){
9804                     var o = ss[i];
9805                     if(o[property].substr(0, vlen).toLowerCase() == value){
9806                         data.push(o);
9807                     }
9808                 }
9809             } else if(value.exec){ // regex?
9810                 for(var i = 0, len = ss.length; i < len; i++){
9811                     var o = ss[i];
9812                     if(value.test(o[property])){
9813                         data.push(o);
9814                     }
9815                 }
9816             } else{
9817                 return;
9818             }
9819             this.jsonData = data;
9820             this.refresh();
9821         }
9822     },
9823
9824 /**
9825  * Filter by a function. The passed function will be called with each
9826  * object in the current dataset. If the function returns true the value is kept,
9827  * otherwise it is filtered.
9828  * @param {Function} fn
9829  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9830  */
9831     filterBy : function(fn, scope){
9832         if(this.jsonData){
9833             var data = [];
9834             var ss = this.snapshot;
9835             for(var i = 0, len = ss.length; i < len; i++){
9836                 var o = ss[i];
9837                 if(fn.call(scope || this, o)){
9838                     data.push(o);
9839                 }
9840             }
9841             this.jsonData = data;
9842             this.refresh();
9843         }
9844     },
9845
9846 /**
9847  * Clears the current filter.
9848  */
9849     clearFilter : function(){
9850         if(this.snapshot && this.jsonData != this.snapshot){
9851             this.jsonData = this.snapshot;
9852             this.refresh();
9853         }
9854     },
9855
9856
9857 /**
9858  * Sorts the data for this view and refreshes it.
9859  * @param {String} property A property on your JSON objects to sort on
9860  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9861  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9862  */
9863     sort : function(property, dir, sortType){
9864         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9865         if(this.jsonData){
9866             var p = property;
9867             var dsc = dir && dir.toLowerCase() == "desc";
9868             var f = function(o1, o2){
9869                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9870                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9871                 ;
9872                 if(v1 < v2){
9873                     return dsc ? +1 : -1;
9874                 } else if(v1 > v2){
9875                     return dsc ? -1 : +1;
9876                 } else{
9877                     return 0;
9878                 }
9879             };
9880             this.jsonData.sort(f);
9881             this.refresh();
9882             if(this.jsonData != this.snapshot){
9883                 this.snapshot.sort(f);
9884             }
9885         }
9886     }
9887 });/*
9888  * Based on:
9889  * Ext JS Library 1.1.1
9890  * Copyright(c) 2006-2007, Ext JS, LLC.
9891  *
9892  * Originally Released Under LGPL - original licence link has changed is not relivant.
9893  *
9894  * Fork - LGPL
9895  * <script type="text/javascript">
9896  */
9897  
9898
9899 /**
9900  * @class Roo.ColorPalette
9901  * @extends Roo.Component
9902  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9903  * Here's an example of typical usage:
9904  * <pre><code>
9905 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9906 cp.render('my-div');
9907
9908 cp.on('select', function(palette, selColor){
9909     // do something with selColor
9910 });
9911 </code></pre>
9912  * @constructor
9913  * Create a new ColorPalette
9914  * @param {Object} config The config object
9915  */
9916 Roo.ColorPalette = function(config){
9917     Roo.ColorPalette.superclass.constructor.call(this, config);
9918     this.addEvents({
9919         /**
9920              * @event select
9921              * Fires when a color is selected
9922              * @param {ColorPalette} this
9923              * @param {String} color The 6-digit color hex code (without the # symbol)
9924              */
9925         select: true
9926     });
9927
9928     if(this.handler){
9929         this.on("select", this.handler, this.scope, true);
9930     }
9931 };
9932 Roo.extend(Roo.ColorPalette, Roo.Component, {
9933     /**
9934      * @cfg {String} itemCls
9935      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9936      */
9937     itemCls : "x-color-palette",
9938     /**
9939      * @cfg {String} value
9940      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9941      * the hex codes are case-sensitive.
9942      */
9943     value : null,
9944     clickEvent:'click',
9945     // private
9946     ctype: "Roo.ColorPalette",
9947
9948     /**
9949      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9950      */
9951     allowReselect : false,
9952
9953     /**
9954      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9955      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9956      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9957      * of colors with the width setting until the box is symmetrical.</p>
9958      * <p>You can override individual colors if needed:</p>
9959      * <pre><code>
9960 var cp = new Roo.ColorPalette();
9961 cp.colors[0] = "FF0000";  // change the first box to red
9962 </code></pre>
9963
9964 Or you can provide a custom array of your own for complete control:
9965 <pre><code>
9966 var cp = new Roo.ColorPalette();
9967 cp.colors = ["000000", "993300", "333300"];
9968 </code></pre>
9969      * @type Array
9970      */
9971     colors : [
9972         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9973         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9974         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9975         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9976         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9977     ],
9978
9979     // private
9980     onRender : function(container, position){
9981         var t = new Roo.MasterTemplate(
9982             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9983         );
9984         var c = this.colors;
9985         for(var i = 0, len = c.length; i < len; i++){
9986             t.add([c[i]]);
9987         }
9988         var el = document.createElement("div");
9989         el.className = this.itemCls;
9990         t.overwrite(el);
9991         container.dom.insertBefore(el, position);
9992         this.el = Roo.get(el);
9993         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9994         if(this.clickEvent != 'click'){
9995             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9996         }
9997     },
9998
9999     // private
10000     afterRender : function(){
10001         Roo.ColorPalette.superclass.afterRender.call(this);
10002         if(this.value){
10003             var s = this.value;
10004             this.value = null;
10005             this.select(s);
10006         }
10007     },
10008
10009     // private
10010     handleClick : function(e, t){
10011         e.preventDefault();
10012         if(!this.disabled){
10013             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10014             this.select(c.toUpperCase());
10015         }
10016     },
10017
10018     /**
10019      * Selects the specified color in the palette (fires the select event)
10020      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10021      */
10022     select : function(color){
10023         color = color.replace("#", "");
10024         if(color != this.value || this.allowReselect){
10025             var el = this.el;
10026             if(this.value){
10027                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10028             }
10029             el.child("a.color-"+color).addClass("x-color-palette-sel");
10030             this.value = color;
10031             this.fireEvent("select", this, color);
10032         }
10033     }
10034 });/*
10035  * Based on:
10036  * Ext JS Library 1.1.1
10037  * Copyright(c) 2006-2007, Ext JS, LLC.
10038  *
10039  * Originally Released Under LGPL - original licence link has changed is not relivant.
10040  *
10041  * Fork - LGPL
10042  * <script type="text/javascript">
10043  */
10044  
10045 /**
10046  * @class Roo.DatePicker
10047  * @extends Roo.Component
10048  * Simple date picker class.
10049  * @constructor
10050  * Create a new DatePicker
10051  * @param {Object} config The config object
10052  */
10053 Roo.DatePicker = function(config){
10054     Roo.DatePicker.superclass.constructor.call(this, config);
10055
10056     this.value = config && config.value ?
10057                  config.value.clearTime() : new Date().clearTime();
10058
10059     this.addEvents({
10060         /**
10061              * @event select
10062              * Fires when a date is selected
10063              * @param {DatePicker} this
10064              * @param {Date} date The selected date
10065              */
10066         select: true
10067     });
10068
10069     if(this.handler){
10070         this.on("select", this.handler,  this.scope || this);
10071     }
10072     // build the disabledDatesRE
10073     if(!this.disabledDatesRE && this.disabledDates){
10074         var dd = this.disabledDates;
10075         var re = "(?:";
10076         for(var i = 0; i < dd.length; i++){
10077             re += dd[i];
10078             if(i != dd.length-1) re += "|";
10079         }
10080         this.disabledDatesRE = new RegExp(re + ")");
10081     }
10082 };
10083
10084 Roo.extend(Roo.DatePicker, Roo.Component, {
10085     /**
10086      * @cfg {String} todayText
10087      * The text to display on the button that selects the current date (defaults to "Today")
10088      */
10089     todayText : "Today",
10090     /**
10091      * @cfg {String} okText
10092      * The text to display on the ok button
10093      */
10094     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10095     /**
10096      * @cfg {String} cancelText
10097      * The text to display on the cancel button
10098      */
10099     cancelText : "Cancel",
10100     /**
10101      * @cfg {String} todayTip
10102      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10103      */
10104     todayTip : "{0} (Spacebar)",
10105     /**
10106      * @cfg {Date} minDate
10107      * Minimum allowable date (JavaScript date object, defaults to null)
10108      */
10109     minDate : null,
10110     /**
10111      * @cfg {Date} maxDate
10112      * Maximum allowable date (JavaScript date object, defaults to null)
10113      */
10114     maxDate : null,
10115     /**
10116      * @cfg {String} minText
10117      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10118      */
10119     minText : "This date is before the minimum date",
10120     /**
10121      * @cfg {String} maxText
10122      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10123      */
10124     maxText : "This date is after the maximum date",
10125     /**
10126      * @cfg {String} format
10127      * The default date format string which can be overriden for localization support.  The format must be
10128      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10129      */
10130     format : "m/d/y",
10131     /**
10132      * @cfg {Array} disabledDays
10133      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10134      */
10135     disabledDays : null,
10136     /**
10137      * @cfg {String} disabledDaysText
10138      * The tooltip to display when the date falls on a disabled day (defaults to "")
10139      */
10140     disabledDaysText : "",
10141     /**
10142      * @cfg {RegExp} disabledDatesRE
10143      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10144      */
10145     disabledDatesRE : null,
10146     /**
10147      * @cfg {String} disabledDatesText
10148      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10149      */
10150     disabledDatesText : "",
10151     /**
10152      * @cfg {Boolean} constrainToViewport
10153      * True to constrain the date picker to the viewport (defaults to true)
10154      */
10155     constrainToViewport : true,
10156     /**
10157      * @cfg {Array} monthNames
10158      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10159      */
10160     monthNames : Date.monthNames,
10161     /**
10162      * @cfg {Array} dayNames
10163      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10164      */
10165     dayNames : Date.dayNames,
10166     /**
10167      * @cfg {String} nextText
10168      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10169      */
10170     nextText: 'Next Month (Control+Right)',
10171     /**
10172      * @cfg {String} prevText
10173      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10174      */
10175     prevText: 'Previous Month (Control+Left)',
10176     /**
10177      * @cfg {String} monthYearText
10178      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10179      */
10180     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10181     /**
10182      * @cfg {Number} startDay
10183      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10184      */
10185     startDay : 0,
10186     /**
10187      * @cfg {Bool} showClear
10188      * Show a clear button (usefull for date form elements that can be blank.)
10189      */
10190     
10191     showClear: false,
10192     
10193     /**
10194      * Sets the value of the date field
10195      * @param {Date} value The date to set
10196      */
10197     setValue : function(value){
10198         var old = this.value;
10199         this.value = value.clearTime(true);
10200         if(this.el){
10201             this.update(this.value);
10202         }
10203     },
10204
10205     /**
10206      * Gets the current selected value of the date field
10207      * @return {Date} The selected date
10208      */
10209     getValue : function(){
10210         return this.value;
10211     },
10212
10213     // private
10214     focus : function(){
10215         if(this.el){
10216             this.update(this.activeDate);
10217         }
10218     },
10219
10220     // private
10221     onRender : function(container, position){
10222         var m = [
10223              '<table cellspacing="0">',
10224                 '<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>',
10225                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10226         var dn = this.dayNames;
10227         for(var i = 0; i < 7; i++){
10228             var d = this.startDay+i;
10229             if(d > 6){
10230                 d = d-7;
10231             }
10232             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10233         }
10234         m[m.length] = "</tr></thead><tbody><tr>";
10235         for(var i = 0; i < 42; i++) {
10236             if(i % 7 == 0 && i != 0){
10237                 m[m.length] = "</tr><tr>";
10238             }
10239             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10240         }
10241         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10242             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10243
10244         var el = document.createElement("div");
10245         el.className = "x-date-picker";
10246         el.innerHTML = m.join("");
10247
10248         container.dom.insertBefore(el, position);
10249
10250         this.el = Roo.get(el);
10251         this.eventEl = Roo.get(el.firstChild);
10252
10253         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10254             handler: this.showPrevMonth,
10255             scope: this,
10256             preventDefault:true,
10257             stopDefault:true
10258         });
10259
10260         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10261             handler: this.showNextMonth,
10262             scope: this,
10263             preventDefault:true,
10264             stopDefault:true
10265         });
10266
10267         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10268
10269         this.monthPicker = this.el.down('div.x-date-mp');
10270         this.monthPicker.enableDisplayMode('block');
10271         
10272         var kn = new Roo.KeyNav(this.eventEl, {
10273             "left" : function(e){
10274                 e.ctrlKey ?
10275                     this.showPrevMonth() :
10276                     this.update(this.activeDate.add("d", -1));
10277             },
10278
10279             "right" : function(e){
10280                 e.ctrlKey ?
10281                     this.showNextMonth() :
10282                     this.update(this.activeDate.add("d", 1));
10283             },
10284
10285             "up" : function(e){
10286                 e.ctrlKey ?
10287                     this.showNextYear() :
10288                     this.update(this.activeDate.add("d", -7));
10289             },
10290
10291             "down" : function(e){
10292                 e.ctrlKey ?
10293                     this.showPrevYear() :
10294                     this.update(this.activeDate.add("d", 7));
10295             },
10296
10297             "pageUp" : function(e){
10298                 this.showNextMonth();
10299             },
10300
10301             "pageDown" : function(e){
10302                 this.showPrevMonth();
10303             },
10304
10305             "enter" : function(e){
10306                 e.stopPropagation();
10307                 return true;
10308             },
10309
10310             scope : this
10311         });
10312
10313         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10314
10315         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10316
10317         this.el.unselectable();
10318         
10319         this.cells = this.el.select("table.x-date-inner tbody td");
10320         this.textNodes = this.el.query("table.x-date-inner tbody span");
10321
10322         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10323             text: "&#160;",
10324             tooltip: this.monthYearText
10325         });
10326
10327         this.mbtn.on('click', this.showMonthPicker, this);
10328         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10329
10330
10331         var today = (new Date()).dateFormat(this.format);
10332         
10333         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10334         if (this.showClear) {
10335             baseTb.add( new Roo.Toolbar.Fill());
10336         }
10337         baseTb.add({
10338             text: String.format(this.todayText, today),
10339             tooltip: String.format(this.todayTip, today),
10340             handler: this.selectToday,
10341             scope: this
10342         });
10343         
10344         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10345             
10346         //});
10347         if (this.showClear) {
10348             
10349             baseTb.add( new Roo.Toolbar.Fill());
10350             baseTb.add({
10351                 text: '&#160;',
10352                 cls: 'x-btn-icon x-btn-clear',
10353                 handler: function() {
10354                     //this.value = '';
10355                     this.fireEvent("select", this, '');
10356                 },
10357                 scope: this
10358             });
10359         }
10360         
10361         
10362         if(Roo.isIE){
10363             this.el.repaint();
10364         }
10365         this.update(this.value);
10366     },
10367
10368     createMonthPicker : function(){
10369         if(!this.monthPicker.dom.firstChild){
10370             var buf = ['<table border="0" cellspacing="0">'];
10371             for(var i = 0; i < 6; i++){
10372                 buf.push(
10373                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10374                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10375                     i == 0 ?
10376                     '<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>' :
10377                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10378                 );
10379             }
10380             buf.push(
10381                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10382                     this.okText,
10383                     '</button><button type="button" class="x-date-mp-cancel">',
10384                     this.cancelText,
10385                     '</button></td></tr>',
10386                 '</table>'
10387             );
10388             this.monthPicker.update(buf.join(''));
10389             this.monthPicker.on('click', this.onMonthClick, this);
10390             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10391
10392             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10393             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10394
10395             this.mpMonths.each(function(m, a, i){
10396                 i += 1;
10397                 if((i%2) == 0){
10398                     m.dom.xmonth = 5 + Math.round(i * .5);
10399                 }else{
10400                     m.dom.xmonth = Math.round((i-1) * .5);
10401                 }
10402             });
10403         }
10404     },
10405
10406     showMonthPicker : function(){
10407         this.createMonthPicker();
10408         var size = this.el.getSize();
10409         this.monthPicker.setSize(size);
10410         this.monthPicker.child('table').setSize(size);
10411
10412         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10413         this.updateMPMonth(this.mpSelMonth);
10414         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10415         this.updateMPYear(this.mpSelYear);
10416
10417         this.monthPicker.slideIn('t', {duration:.2});
10418     },
10419
10420     updateMPYear : function(y){
10421         this.mpyear = y;
10422         var ys = this.mpYears.elements;
10423         for(var i = 1; i <= 10; i++){
10424             var td = ys[i-1], y2;
10425             if((i%2) == 0){
10426                 y2 = y + Math.round(i * .5);
10427                 td.firstChild.innerHTML = y2;
10428                 td.xyear = y2;
10429             }else{
10430                 y2 = y - (5-Math.round(i * .5));
10431                 td.firstChild.innerHTML = y2;
10432                 td.xyear = y2;
10433             }
10434             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10435         }
10436     },
10437
10438     updateMPMonth : function(sm){
10439         this.mpMonths.each(function(m, a, i){
10440             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10441         });
10442     },
10443
10444     selectMPMonth: function(m){
10445         
10446     },
10447
10448     onMonthClick : function(e, t){
10449         e.stopEvent();
10450         var el = new Roo.Element(t), pn;
10451         if(el.is('button.x-date-mp-cancel')){
10452             this.hideMonthPicker();
10453         }
10454         else if(el.is('button.x-date-mp-ok')){
10455             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10456             this.hideMonthPicker();
10457         }
10458         else if(pn = el.up('td.x-date-mp-month', 2)){
10459             this.mpMonths.removeClass('x-date-mp-sel');
10460             pn.addClass('x-date-mp-sel');
10461             this.mpSelMonth = pn.dom.xmonth;
10462         }
10463         else if(pn = el.up('td.x-date-mp-year', 2)){
10464             this.mpYears.removeClass('x-date-mp-sel');
10465             pn.addClass('x-date-mp-sel');
10466             this.mpSelYear = pn.dom.xyear;
10467         }
10468         else if(el.is('a.x-date-mp-prev')){
10469             this.updateMPYear(this.mpyear-10);
10470         }
10471         else if(el.is('a.x-date-mp-next')){
10472             this.updateMPYear(this.mpyear+10);
10473         }
10474     },
10475
10476     onMonthDblClick : function(e, t){
10477         e.stopEvent();
10478         var el = new Roo.Element(t), pn;
10479         if(pn = el.up('td.x-date-mp-month', 2)){
10480             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10481             this.hideMonthPicker();
10482         }
10483         else if(pn = el.up('td.x-date-mp-year', 2)){
10484             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10485             this.hideMonthPicker();
10486         }
10487     },
10488
10489     hideMonthPicker : function(disableAnim){
10490         if(this.monthPicker){
10491             if(disableAnim === true){
10492                 this.monthPicker.hide();
10493             }else{
10494                 this.monthPicker.slideOut('t', {duration:.2});
10495             }
10496         }
10497     },
10498
10499     // private
10500     showPrevMonth : function(e){
10501         this.update(this.activeDate.add("mo", -1));
10502     },
10503
10504     // private
10505     showNextMonth : function(e){
10506         this.update(this.activeDate.add("mo", 1));
10507     },
10508
10509     // private
10510     showPrevYear : function(){
10511         this.update(this.activeDate.add("y", -1));
10512     },
10513
10514     // private
10515     showNextYear : function(){
10516         this.update(this.activeDate.add("y", 1));
10517     },
10518
10519     // private
10520     handleMouseWheel : function(e){
10521         var delta = e.getWheelDelta();
10522         if(delta > 0){
10523             this.showPrevMonth();
10524             e.stopEvent();
10525         } else if(delta < 0){
10526             this.showNextMonth();
10527             e.stopEvent();
10528         }
10529     },
10530
10531     // private
10532     handleDateClick : function(e, t){
10533         e.stopEvent();
10534         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10535             this.setValue(new Date(t.dateValue));
10536             this.fireEvent("select", this, this.value);
10537         }
10538     },
10539
10540     // private
10541     selectToday : function(){
10542         this.setValue(new Date().clearTime());
10543         this.fireEvent("select", this, this.value);
10544     },
10545
10546     // private
10547     update : function(date){
10548         var vd = this.activeDate;
10549         this.activeDate = date;
10550         if(vd && this.el){
10551             var t = date.getTime();
10552             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10553                 this.cells.removeClass("x-date-selected");
10554                 this.cells.each(function(c){
10555                    if(c.dom.firstChild.dateValue == t){
10556                        c.addClass("x-date-selected");
10557                        setTimeout(function(){
10558                             try{c.dom.firstChild.focus();}catch(e){}
10559                        }, 50);
10560                        return false;
10561                    }
10562                 });
10563                 return;
10564             }
10565         }
10566         var days = date.getDaysInMonth();
10567         var firstOfMonth = date.getFirstDateOfMonth();
10568         var startingPos = firstOfMonth.getDay()-this.startDay;
10569
10570         if(startingPos <= this.startDay){
10571             startingPos += 7;
10572         }
10573
10574         var pm = date.add("mo", -1);
10575         var prevStart = pm.getDaysInMonth()-startingPos;
10576
10577         var cells = this.cells.elements;
10578         var textEls = this.textNodes;
10579         days += startingPos;
10580
10581         // convert everything to numbers so it's fast
10582         var day = 86400000;
10583         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10584         var today = new Date().clearTime().getTime();
10585         var sel = date.clearTime().getTime();
10586         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10587         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10588         var ddMatch = this.disabledDatesRE;
10589         var ddText = this.disabledDatesText;
10590         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10591         var ddaysText = this.disabledDaysText;
10592         var format = this.format;
10593
10594         var setCellClass = function(cal, cell){
10595             cell.title = "";
10596             var t = d.getTime();
10597             cell.firstChild.dateValue = t;
10598             if(t == today){
10599                 cell.className += " x-date-today";
10600                 cell.title = cal.todayText;
10601             }
10602             if(t == sel){
10603                 cell.className += " x-date-selected";
10604                 setTimeout(function(){
10605                     try{cell.firstChild.focus();}catch(e){}
10606                 }, 50);
10607             }
10608             // disabling
10609             if(t < min) {
10610                 cell.className = " x-date-disabled";
10611                 cell.title = cal.minText;
10612                 return;
10613             }
10614             if(t > max) {
10615                 cell.className = " x-date-disabled";
10616                 cell.title = cal.maxText;
10617                 return;
10618             }
10619             if(ddays){
10620                 if(ddays.indexOf(d.getDay()) != -1){
10621                     cell.title = ddaysText;
10622                     cell.className = " x-date-disabled";
10623                 }
10624             }
10625             if(ddMatch && format){
10626                 var fvalue = d.dateFormat(format);
10627                 if(ddMatch.test(fvalue)){
10628                     cell.title = ddText.replace("%0", fvalue);
10629                     cell.className = " x-date-disabled";
10630                 }
10631             }
10632         };
10633
10634         var i = 0;
10635         for(; i < startingPos; i++) {
10636             textEls[i].innerHTML = (++prevStart);
10637             d.setDate(d.getDate()+1);
10638             cells[i].className = "x-date-prevday";
10639             setCellClass(this, cells[i]);
10640         }
10641         for(; i < days; i++){
10642             intDay = i - startingPos + 1;
10643             textEls[i].innerHTML = (intDay);
10644             d.setDate(d.getDate()+1);
10645             cells[i].className = "x-date-active";
10646             setCellClass(this, cells[i]);
10647         }
10648         var extraDays = 0;
10649         for(; i < 42; i++) {
10650              textEls[i].innerHTML = (++extraDays);
10651              d.setDate(d.getDate()+1);
10652              cells[i].className = "x-date-nextday";
10653              setCellClass(this, cells[i]);
10654         }
10655
10656         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10657
10658         if(!this.internalRender){
10659             var main = this.el.dom.firstChild;
10660             var w = main.offsetWidth;
10661             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10662             Roo.fly(main).setWidth(w);
10663             this.internalRender = true;
10664             // opera does not respect the auto grow header center column
10665             // then, after it gets a width opera refuses to recalculate
10666             // without a second pass
10667             if(Roo.isOpera && !this.secondPass){
10668                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10669                 this.secondPass = true;
10670                 this.update.defer(10, this, [date]);
10671             }
10672         }
10673     }
10674 });/*
10675  * Based on:
10676  * Ext JS Library 1.1.1
10677  * Copyright(c) 2006-2007, Ext JS, LLC.
10678  *
10679  * Originally Released Under LGPL - original licence link has changed is not relivant.
10680  *
10681  * Fork - LGPL
10682  * <script type="text/javascript">
10683  */
10684 /**
10685  * @class Roo.TabPanel
10686  * @extends Roo.util.Observable
10687  * A lightweight tab container.
10688  * <br><br>
10689  * Usage:
10690  * <pre><code>
10691 // basic tabs 1, built from existing content
10692 var tabs = new Roo.TabPanel("tabs1");
10693 tabs.addTab("script", "View Script");
10694 tabs.addTab("markup", "View Markup");
10695 tabs.activate("script");
10696
10697 // more advanced tabs, built from javascript
10698 var jtabs = new Roo.TabPanel("jtabs");
10699 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10700
10701 // set up the UpdateManager
10702 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10703 var updater = tab2.getUpdateManager();
10704 updater.setDefaultUrl("ajax1.htm");
10705 tab2.on('activate', updater.refresh, updater, true);
10706
10707 // Use setUrl for Ajax loading
10708 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10709 tab3.setUrl("ajax2.htm", null, true);
10710
10711 // Disabled tab
10712 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10713 tab4.disable();
10714
10715 jtabs.activate("jtabs-1");
10716  * </code></pre>
10717  * @constructor
10718  * Create a new TabPanel.
10719  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10720  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10721  */
10722 Roo.TabPanel = function(container, config){
10723     /**
10724     * The container element for this TabPanel.
10725     * @type Roo.Element
10726     */
10727     this.el = Roo.get(container, true);
10728     if(config){
10729         if(typeof config == "boolean"){
10730             this.tabPosition = config ? "bottom" : "top";
10731         }else{
10732             Roo.apply(this, config);
10733         }
10734     }
10735     if(this.tabPosition == "bottom"){
10736         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10737         this.el.addClass("x-tabs-bottom");
10738     }
10739     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10740     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10741     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10742     if(Roo.isIE){
10743         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10744     }
10745     if(this.tabPosition != "bottom"){
10746     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10747      * @type Roo.Element
10748      */
10749       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10750       this.el.addClass("x-tabs-top");
10751     }
10752     this.items = [];
10753
10754     this.bodyEl.setStyle("position", "relative");
10755
10756     this.active = null;
10757     this.activateDelegate = this.activate.createDelegate(this);
10758
10759     this.addEvents({
10760         /**
10761          * @event tabchange
10762          * Fires when the active tab changes
10763          * @param {Roo.TabPanel} this
10764          * @param {Roo.TabPanelItem} activePanel The new active tab
10765          */
10766         "tabchange": true,
10767         /**
10768          * @event beforetabchange
10769          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10770          * @param {Roo.TabPanel} this
10771          * @param {Object} e Set cancel to true on this object to cancel the tab change
10772          * @param {Roo.TabPanelItem} tab The tab being changed to
10773          */
10774         "beforetabchange" : true
10775     });
10776
10777     Roo.EventManager.onWindowResize(this.onResize, this);
10778     this.cpad = this.el.getPadding("lr");
10779     this.hiddenCount = 0;
10780
10781     Roo.TabPanel.superclass.constructor.call(this);
10782 };
10783
10784 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10785         /*
10786          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10787          */
10788     tabPosition : "top",
10789         /*
10790          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10791          */
10792     currentTabWidth : 0,
10793         /*
10794          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10795          */
10796     minTabWidth : 40,
10797         /*
10798          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10799          */
10800     maxTabWidth : 250,
10801         /*
10802          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10803          */
10804     preferredTabWidth : 175,
10805         /*
10806          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10807          */
10808     resizeTabs : false,
10809         /*
10810          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10811          */
10812     monitorResize : true,
10813
10814     /**
10815      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10816      * @param {String} id The id of the div to use <b>or create</b>
10817      * @param {String} text The text for the tab
10818      * @param {String} content (optional) Content to put in the TabPanelItem body
10819      * @param {Boolean} closable (optional) True to create a close icon on the tab
10820      * @return {Roo.TabPanelItem} The created TabPanelItem
10821      */
10822     addTab : function(id, text, content, closable){
10823         var item = new Roo.TabPanelItem(this, id, text, closable);
10824         this.addTabItem(item);
10825         if(content){
10826             item.setContent(content);
10827         }
10828         return item;
10829     },
10830
10831     /**
10832      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10833      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10834      * @return {Roo.TabPanelItem}
10835      */
10836     getTab : function(id){
10837         return this.items[id];
10838     },
10839
10840     /**
10841      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10842      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10843      */
10844     hideTab : function(id){
10845         var t = this.items[id];
10846         if(!t.isHidden()){
10847            t.setHidden(true);
10848            this.hiddenCount++;
10849            this.autoSizeTabs();
10850         }
10851     },
10852
10853     /**
10854      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10855      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10856      */
10857     unhideTab : function(id){
10858         var t = this.items[id];
10859         if(t.isHidden()){
10860            t.setHidden(false);
10861            this.hiddenCount--;
10862            this.autoSizeTabs();
10863         }
10864     },
10865
10866     /**
10867      * Adds an existing {@link Roo.TabPanelItem}.
10868      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10869      */
10870     addTabItem : function(item){
10871         this.items[item.id] = item;
10872         this.items.push(item);
10873         if(this.resizeTabs){
10874            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10875            this.autoSizeTabs();
10876         }else{
10877             item.autoSize();
10878         }
10879     },
10880
10881     /**
10882      * Removes a {@link Roo.TabPanelItem}.
10883      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10884      */
10885     removeTab : function(id){
10886         var items = this.items;
10887         var tab = items[id];
10888         if(!tab) { return; }
10889         var index = items.indexOf(tab);
10890         if(this.active == tab && items.length > 1){
10891             var newTab = this.getNextAvailable(index);
10892             if(newTab) {
10893                 newTab.activate();
10894             }
10895         }
10896         this.stripEl.dom.removeChild(tab.pnode.dom);
10897         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10898             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10899         }
10900         items.splice(index, 1);
10901         delete this.items[tab.id];
10902         tab.fireEvent("close", tab);
10903         tab.purgeListeners();
10904         this.autoSizeTabs();
10905     },
10906
10907     getNextAvailable : function(start){
10908         var items = this.items;
10909         var index = start;
10910         // look for a next tab that will slide over to
10911         // replace the one being removed
10912         while(index < items.length){
10913             var item = items[++index];
10914             if(item && !item.isHidden()){
10915                 return item;
10916             }
10917         }
10918         // if one isn't found select the previous tab (on the left)
10919         index = start;
10920         while(index >= 0){
10921             var item = items[--index];
10922             if(item && !item.isHidden()){
10923                 return item;
10924             }
10925         }
10926         return null;
10927     },
10928
10929     /**
10930      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10931      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10932      */
10933     disableTab : function(id){
10934         var tab = this.items[id];
10935         if(tab && this.active != tab){
10936             tab.disable();
10937         }
10938     },
10939
10940     /**
10941      * Enables a {@link Roo.TabPanelItem} that is disabled.
10942      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10943      */
10944     enableTab : function(id){
10945         var tab = this.items[id];
10946         tab.enable();
10947     },
10948
10949     /**
10950      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10951      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10952      * @return {Roo.TabPanelItem} The TabPanelItem.
10953      */
10954     activate : function(id){
10955         var tab = this.items[id];
10956         if(!tab){
10957             return null;
10958         }
10959         if(tab == this.active || tab.disabled){
10960             return tab;
10961         }
10962         var e = {};
10963         this.fireEvent("beforetabchange", this, e, tab);
10964         if(e.cancel !== true && !tab.disabled){
10965             if(this.active){
10966                 this.active.hide();
10967             }
10968             this.active = this.items[id];
10969             this.active.show();
10970             this.fireEvent("tabchange", this, this.active);
10971         }
10972         return tab;
10973     },
10974
10975     /**
10976      * Gets the active {@link Roo.TabPanelItem}.
10977      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10978      */
10979     getActiveTab : function(){
10980         return this.active;
10981     },
10982
10983     /**
10984      * Updates the tab body element to fit the height of the container element
10985      * for overflow scrolling
10986      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10987      */
10988     syncHeight : function(targetHeight){
10989         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10990         var bm = this.bodyEl.getMargins();
10991         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10992         this.bodyEl.setHeight(newHeight);
10993         return newHeight;
10994     },
10995
10996     onResize : function(){
10997         if(this.monitorResize){
10998             this.autoSizeTabs();
10999         }
11000     },
11001
11002     /**
11003      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11004      */
11005     beginUpdate : function(){
11006         this.updating = true;
11007     },
11008
11009     /**
11010      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11011      */
11012     endUpdate : function(){
11013         this.updating = false;
11014         this.autoSizeTabs();
11015     },
11016
11017     /**
11018      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11019      */
11020     autoSizeTabs : function(){
11021         var count = this.items.length;
11022         var vcount = count - this.hiddenCount;
11023         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11024         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11025         var availWidth = Math.floor(w / vcount);
11026         var b = this.stripBody;
11027         if(b.getWidth() > w){
11028             var tabs = this.items;
11029             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11030             if(availWidth < this.minTabWidth){
11031                 /*if(!this.sleft){    // incomplete scrolling code
11032                     this.createScrollButtons();
11033                 }
11034                 this.showScroll();
11035                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11036             }
11037         }else{
11038             if(this.currentTabWidth < this.preferredTabWidth){
11039                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11040             }
11041         }
11042     },
11043
11044     /**
11045      * Returns the number of tabs in this TabPanel.
11046      * @return {Number}
11047      */
11048      getCount : function(){
11049          return this.items.length;
11050      },
11051
11052     /**
11053      * Resizes all the tabs to the passed width
11054      * @param {Number} The new width
11055      */
11056     setTabWidth : function(width){
11057         this.currentTabWidth = width;
11058         for(var i = 0, len = this.items.length; i < len; i++) {
11059                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11060         }
11061     },
11062
11063     /**
11064      * Destroys this TabPanel
11065      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11066      */
11067     destroy : function(removeEl){
11068         Roo.EventManager.removeResizeListener(this.onResize, this);
11069         for(var i = 0, len = this.items.length; i < len; i++){
11070             this.items[i].purgeListeners();
11071         }
11072         if(removeEl === true){
11073             this.el.update("");
11074             this.el.remove();
11075         }
11076     }
11077 });
11078
11079 /**
11080  * @class Roo.TabPanelItem
11081  * @extends Roo.util.Observable
11082  * Represents an individual item (tab plus body) in a TabPanel.
11083  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11084  * @param {String} id The id of this TabPanelItem
11085  * @param {String} text The text for the tab of this TabPanelItem
11086  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11087  */
11088 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11089     /**
11090      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11091      * @type Roo.TabPanel
11092      */
11093     this.tabPanel = tabPanel;
11094     /**
11095      * The id for this TabPanelItem
11096      * @type String
11097      */
11098     this.id = id;
11099     /** @private */
11100     this.disabled = false;
11101     /** @private */
11102     this.text = text;
11103     /** @private */
11104     this.loaded = false;
11105     this.closable = closable;
11106
11107     /**
11108      * The body element for this TabPanelItem.
11109      * @type Roo.Element
11110      */
11111     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11112     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11113     this.bodyEl.setStyle("display", "block");
11114     this.bodyEl.setStyle("zoom", "1");
11115     this.hideAction();
11116
11117     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11118     /** @private */
11119     this.el = Roo.get(els.el, true);
11120     this.inner = Roo.get(els.inner, true);
11121     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11122     this.pnode = Roo.get(els.el.parentNode, true);
11123     this.el.on("mousedown", this.onTabMouseDown, this);
11124     this.el.on("click", this.onTabClick, this);
11125     /** @private */
11126     if(closable){
11127         var c = Roo.get(els.close, true);
11128         c.dom.title = this.closeText;
11129         c.addClassOnOver("close-over");
11130         c.on("click", this.closeClick, this);
11131      }
11132
11133     this.addEvents({
11134          /**
11135          * @event activate
11136          * Fires when this tab becomes the active tab.
11137          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11138          * @param {Roo.TabPanelItem} this
11139          */
11140         "activate": true,
11141         /**
11142          * @event beforeclose
11143          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11144          * @param {Roo.TabPanelItem} this
11145          * @param {Object} e Set cancel to true on this object to cancel the close.
11146          */
11147         "beforeclose": true,
11148         /**
11149          * @event close
11150          * Fires when this tab is closed.
11151          * @param {Roo.TabPanelItem} this
11152          */
11153          "close": true,
11154         /**
11155          * @event deactivate
11156          * Fires when this tab is no longer the active tab.
11157          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11158          * @param {Roo.TabPanelItem} this
11159          */
11160          "deactivate" : true
11161     });
11162     this.hidden = false;
11163
11164     Roo.TabPanelItem.superclass.constructor.call(this);
11165 };
11166
11167 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11168     purgeListeners : function(){
11169        Roo.util.Observable.prototype.purgeListeners.call(this);
11170        this.el.removeAllListeners();
11171     },
11172     /**
11173      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11174      */
11175     show : function(){
11176         this.pnode.addClass("on");
11177         this.showAction();
11178         if(Roo.isOpera){
11179             this.tabPanel.stripWrap.repaint();
11180         }
11181         this.fireEvent("activate", this.tabPanel, this);
11182     },
11183
11184     /**
11185      * Returns true if this tab is the active tab.
11186      * @return {Boolean}
11187      */
11188     isActive : function(){
11189         return this.tabPanel.getActiveTab() == this;
11190     },
11191
11192     /**
11193      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11194      */
11195     hide : function(){
11196         this.pnode.removeClass("on");
11197         this.hideAction();
11198         this.fireEvent("deactivate", this.tabPanel, this);
11199     },
11200
11201     hideAction : function(){
11202         this.bodyEl.hide();
11203         this.bodyEl.setStyle("position", "absolute");
11204         this.bodyEl.setLeft("-20000px");
11205         this.bodyEl.setTop("-20000px");
11206     },
11207
11208     showAction : function(){
11209         this.bodyEl.setStyle("position", "relative");
11210         this.bodyEl.setTop("");
11211         this.bodyEl.setLeft("");
11212         this.bodyEl.show();
11213     },
11214
11215     /**
11216      * Set the tooltip for the tab.
11217      * @param {String} tooltip The tab's tooltip
11218      */
11219     setTooltip : function(text){
11220         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11221             this.textEl.dom.qtip = text;
11222             this.textEl.dom.removeAttribute('title');
11223         }else{
11224             this.textEl.dom.title = text;
11225         }
11226     },
11227
11228     onTabClick : function(e){
11229         e.preventDefault();
11230         this.tabPanel.activate(this.id);
11231     },
11232
11233     onTabMouseDown : function(e){
11234         e.preventDefault();
11235         this.tabPanel.activate(this.id);
11236     },
11237
11238     getWidth : function(){
11239         return this.inner.getWidth();
11240     },
11241
11242     setWidth : function(width){
11243         var iwidth = width - this.pnode.getPadding("lr");
11244         this.inner.setWidth(iwidth);
11245         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11246         this.pnode.setWidth(width);
11247     },
11248
11249     /**
11250      * Show or hide the tab
11251      * @param {Boolean} hidden True to hide or false to show.
11252      */
11253     setHidden : function(hidden){
11254         this.hidden = hidden;
11255         this.pnode.setStyle("display", hidden ? "none" : "");
11256     },
11257
11258     /**
11259      * Returns true if this tab is "hidden"
11260      * @return {Boolean}
11261      */
11262     isHidden : function(){
11263         return this.hidden;
11264     },
11265
11266     /**
11267      * Returns the text for this tab
11268      * @return {String}
11269      */
11270     getText : function(){
11271         return this.text;
11272     },
11273
11274     autoSize : function(){
11275         //this.el.beginMeasure();
11276         this.textEl.setWidth(1);
11277         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11278         //this.el.endMeasure();
11279     },
11280
11281     /**
11282      * Sets the text for the tab (Note: this also sets the tooltip text)
11283      * @param {String} text The tab's text and tooltip
11284      */
11285     setText : function(text){
11286         this.text = text;
11287         this.textEl.update(text);
11288         this.setTooltip(text);
11289         if(!this.tabPanel.resizeTabs){
11290             this.autoSize();
11291         }
11292     },
11293     /**
11294      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11295      */
11296     activate : function(){
11297         this.tabPanel.activate(this.id);
11298     },
11299
11300     /**
11301      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11302      */
11303     disable : function(){
11304         if(this.tabPanel.active != this){
11305             this.disabled = true;
11306             this.pnode.addClass("disabled");
11307         }
11308     },
11309
11310     /**
11311      * Enables this TabPanelItem if it was previously disabled.
11312      */
11313     enable : function(){
11314         this.disabled = false;
11315         this.pnode.removeClass("disabled");
11316     },
11317
11318     /**
11319      * Sets the content for this TabPanelItem.
11320      * @param {String} content The content
11321      * @param {Boolean} loadScripts true to look for and load scripts
11322      */
11323     setContent : function(content, loadScripts){
11324         this.bodyEl.update(content, loadScripts);
11325     },
11326
11327     /**
11328      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11329      * @return {Roo.UpdateManager} The UpdateManager
11330      */
11331     getUpdateManager : function(){
11332         return this.bodyEl.getUpdateManager();
11333     },
11334
11335     /**
11336      * Set a URL to be used to load the content for this TabPanelItem.
11337      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11338      * @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)
11339      * @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)
11340      * @return {Roo.UpdateManager} The UpdateManager
11341      */
11342     setUrl : function(url, params, loadOnce){
11343         if(this.refreshDelegate){
11344             this.un('activate', this.refreshDelegate);
11345         }
11346         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11347         this.on("activate", this.refreshDelegate);
11348         return this.bodyEl.getUpdateManager();
11349     },
11350
11351     /** @private */
11352     _handleRefresh : function(url, params, loadOnce){
11353         if(!loadOnce || !this.loaded){
11354             var updater = this.bodyEl.getUpdateManager();
11355             updater.update(url, params, this._setLoaded.createDelegate(this));
11356         }
11357     },
11358
11359     /**
11360      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11361      *   Will fail silently if the setUrl method has not been called.
11362      *   This does not activate the panel, just updates its content.
11363      */
11364     refresh : function(){
11365         if(this.refreshDelegate){
11366            this.loaded = false;
11367            this.refreshDelegate();
11368         }
11369     },
11370
11371     /** @private */
11372     _setLoaded : function(){
11373         this.loaded = true;
11374     },
11375
11376     /** @private */
11377     closeClick : function(e){
11378         var o = {};
11379         e.stopEvent();
11380         this.fireEvent("beforeclose", this, o);
11381         if(o.cancel !== true){
11382             this.tabPanel.removeTab(this.id);
11383         }
11384     },
11385     /**
11386      * The text displayed in the tooltip for the close icon.
11387      * @type String
11388      */
11389     closeText : "Close this tab"
11390 });
11391
11392 /** @private */
11393 Roo.TabPanel.prototype.createStrip = function(container){
11394     var strip = document.createElement("div");
11395     strip.className = "x-tabs-wrap";
11396     container.appendChild(strip);
11397     return strip;
11398 };
11399 /** @private */
11400 Roo.TabPanel.prototype.createStripList = function(strip){
11401     // div wrapper for retard IE
11402     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
11403     return strip.firstChild.firstChild.firstChild.firstChild;
11404 };
11405 /** @private */
11406 Roo.TabPanel.prototype.createBody = function(container){
11407     var body = document.createElement("div");
11408     Roo.id(body, "tab-body");
11409     Roo.fly(body).addClass("x-tabs-body");
11410     container.appendChild(body);
11411     return body;
11412 };
11413 /** @private */
11414 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11415     var body = Roo.getDom(id);
11416     if(!body){
11417         body = document.createElement("div");
11418         body.id = id;
11419     }
11420     Roo.fly(body).addClass("x-tabs-item-body");
11421     bodyEl.insertBefore(body, bodyEl.firstChild);
11422     return body;
11423 };
11424 /** @private */
11425 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11426     var td = document.createElement("td");
11427     stripEl.appendChild(td);
11428     if(closable){
11429         td.className = "x-tabs-closable";
11430         if(!this.closeTpl){
11431             this.closeTpl = new Roo.Template(
11432                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11433                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11434                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11435             );
11436         }
11437         var el = this.closeTpl.overwrite(td, {"text": text});
11438         var close = el.getElementsByTagName("div")[0];
11439         var inner = el.getElementsByTagName("em")[0];
11440         return {"el": el, "close": close, "inner": inner};
11441     } else {
11442         if(!this.tabTpl){
11443             this.tabTpl = new Roo.Template(
11444                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11445                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11446             );
11447         }
11448         var el = this.tabTpl.overwrite(td, {"text": text});
11449         var inner = el.getElementsByTagName("em")[0];
11450         return {"el": el, "inner": inner};
11451     }
11452 };/*
11453  * Based on:
11454  * Ext JS Library 1.1.1
11455  * Copyright(c) 2006-2007, Ext JS, LLC.
11456  *
11457  * Originally Released Under LGPL - original licence link has changed is not relivant.
11458  *
11459  * Fork - LGPL
11460  * <script type="text/javascript">
11461  */
11462
11463 /**
11464  * @class Roo.Button
11465  * @extends Roo.util.Observable
11466  * Simple Button class
11467  * @cfg {String} text The button text
11468  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11469  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11470  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11471  * @cfg {Object} scope The scope of the handler
11472  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11473  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11474  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11475  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11476  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11477  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11478    applies if enableToggle = true)
11479  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11480  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11481   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11482  * @constructor
11483  * Create a new button
11484  * @param {Object} config The config object
11485  */
11486 Roo.Button = function(renderTo, config)
11487 {
11488     if (!config) {
11489         config = renderTo;
11490         renderTo = config.renderTo || false;
11491     }
11492     
11493     Roo.apply(this, config);
11494     this.addEvents({
11495         /**
11496              * @event click
11497              * Fires when this button is clicked
11498              * @param {Button} this
11499              * @param {EventObject} e The click event
11500              */
11501             "click" : true,
11502         /**
11503              * @event toggle
11504              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11505              * @param {Button} this
11506              * @param {Boolean} pressed
11507              */
11508             "toggle" : true,
11509         /**
11510              * @event mouseover
11511              * Fires when the mouse hovers over the button
11512              * @param {Button} this
11513              * @param {Event} e The event object
11514              */
11515         'mouseover' : true,
11516         /**
11517              * @event mouseout
11518              * Fires when the mouse exits the button
11519              * @param {Button} this
11520              * @param {Event} e The event object
11521              */
11522         'mouseout': true,
11523          /**
11524              * @event render
11525              * Fires when the button is rendered
11526              * @param {Button} this
11527              */
11528         'render': true
11529     });
11530     if(this.menu){
11531         this.menu = Roo.menu.MenuMgr.get(this.menu);
11532     }
11533     // register listeners first!!  - so render can be captured..
11534     Roo.util.Observable.call(this);
11535     if(renderTo){
11536         this.render(renderTo);
11537     }
11538     
11539   
11540 };
11541
11542 Roo.extend(Roo.Button, Roo.util.Observable, {
11543     /**
11544      * 
11545      */
11546     
11547     /**
11548      * Read-only. True if this button is hidden
11549      * @type Boolean
11550      */
11551     hidden : false,
11552     /**
11553      * Read-only. True if this button is disabled
11554      * @type Boolean
11555      */
11556     disabled : false,
11557     /**
11558      * Read-only. True if this button is pressed (only if enableToggle = true)
11559      * @type Boolean
11560      */
11561     pressed : false,
11562
11563     /**
11564      * @cfg {Number} tabIndex 
11565      * The DOM tabIndex for this button (defaults to undefined)
11566      */
11567     tabIndex : undefined,
11568
11569     /**
11570      * @cfg {Boolean} enableToggle
11571      * True to enable pressed/not pressed toggling (defaults to false)
11572      */
11573     enableToggle: false,
11574     /**
11575      * @cfg {Mixed} menu
11576      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11577      */
11578     menu : undefined,
11579     /**
11580      * @cfg {String} menuAlign
11581      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11582      */
11583     menuAlign : "tl-bl?",
11584
11585     /**
11586      * @cfg {String} iconCls
11587      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11588      */
11589     iconCls : undefined,
11590     /**
11591      * @cfg {String} type
11592      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11593      */
11594     type : 'button',
11595
11596     // private
11597     menuClassTarget: 'tr',
11598
11599     /**
11600      * @cfg {String} clickEvent
11601      * The type of event to map to the button's event handler (defaults to 'click')
11602      */
11603     clickEvent : 'click',
11604
11605     /**
11606      * @cfg {Boolean} handleMouseEvents
11607      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11608      */
11609     handleMouseEvents : true,
11610
11611     /**
11612      * @cfg {String} tooltipType
11613      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11614      */
11615     tooltipType : 'qtip',
11616
11617     /**
11618      * @cfg {String} cls
11619      * A CSS class to apply to the button's main element.
11620      */
11621     
11622     /**
11623      * @cfg {Roo.Template} template (Optional)
11624      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11625      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11626      * require code modifications if required elements (e.g. a button) aren't present.
11627      */
11628
11629     // private
11630     render : function(renderTo){
11631         var btn;
11632         if(this.hideParent){
11633             this.parentEl = Roo.get(renderTo);
11634         }
11635         if(!this.dhconfig){
11636             if(!this.template){
11637                 if(!Roo.Button.buttonTemplate){
11638                     // hideous table template
11639                     Roo.Button.buttonTemplate = new Roo.Template(
11640                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11641                         '<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>',
11642                         "</tr></tbody></table>");
11643                 }
11644                 this.template = Roo.Button.buttonTemplate;
11645             }
11646             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11647             var btnEl = btn.child("button:first");
11648             btnEl.on('focus', this.onFocus, this);
11649             btnEl.on('blur', this.onBlur, this);
11650             if(this.cls){
11651                 btn.addClass(this.cls);
11652             }
11653             if(this.icon){
11654                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11655             }
11656             if(this.iconCls){
11657                 btnEl.addClass(this.iconCls);
11658                 if(!this.cls){
11659                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11660                 }
11661             }
11662             if(this.tabIndex !== undefined){
11663                 btnEl.dom.tabIndex = this.tabIndex;
11664             }
11665             if(this.tooltip){
11666                 if(typeof this.tooltip == 'object'){
11667                     Roo.QuickTips.tips(Roo.apply({
11668                           target: btnEl.id
11669                     }, this.tooltip));
11670                 } else {
11671                     btnEl.dom[this.tooltipType] = this.tooltip;
11672                 }
11673             }
11674         }else{
11675             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11676         }
11677         this.el = btn;
11678         if(this.id){
11679             this.el.dom.id = this.el.id = this.id;
11680         }
11681         if(this.menu){
11682             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11683             this.menu.on("show", this.onMenuShow, this);
11684             this.menu.on("hide", this.onMenuHide, this);
11685         }
11686         btn.addClass("x-btn");
11687         if(Roo.isIE && !Roo.isIE7){
11688             this.autoWidth.defer(1, this);
11689         }else{
11690             this.autoWidth();
11691         }
11692         if(this.handleMouseEvents){
11693             btn.on("mouseover", this.onMouseOver, this);
11694             btn.on("mouseout", this.onMouseOut, this);
11695             btn.on("mousedown", this.onMouseDown, this);
11696         }
11697         btn.on(this.clickEvent, this.onClick, this);
11698         //btn.on("mouseup", this.onMouseUp, this);
11699         if(this.hidden){
11700             this.hide();
11701         }
11702         if(this.disabled){
11703             this.disable();
11704         }
11705         Roo.ButtonToggleMgr.register(this);
11706         if(this.pressed){
11707             this.el.addClass("x-btn-pressed");
11708         }
11709         if(this.repeat){
11710             var repeater = new Roo.util.ClickRepeater(btn,
11711                 typeof this.repeat == "object" ? this.repeat : {}
11712             );
11713             repeater.on("click", this.onClick,  this);
11714         }
11715         
11716         this.fireEvent('render', this);
11717         
11718     },
11719     /**
11720      * Returns the button's underlying element
11721      * @return {Roo.Element} The element
11722      */
11723     getEl : function(){
11724         return this.el;  
11725     },
11726     
11727     /**
11728      * Destroys this Button and removes any listeners.
11729      */
11730     destroy : function(){
11731         Roo.ButtonToggleMgr.unregister(this);
11732         this.el.removeAllListeners();
11733         this.purgeListeners();
11734         this.el.remove();
11735     },
11736
11737     // private
11738     autoWidth : function(){
11739         if(this.el){
11740             this.el.setWidth("auto");
11741             if(Roo.isIE7 && Roo.isStrict){
11742                 var ib = this.el.child('button');
11743                 if(ib && ib.getWidth() > 20){
11744                     ib.clip();
11745                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11746                 }
11747             }
11748             if(this.minWidth){
11749                 if(this.hidden){
11750                     this.el.beginMeasure();
11751                 }
11752                 if(this.el.getWidth() < this.minWidth){
11753                     this.el.setWidth(this.minWidth);
11754                 }
11755                 if(this.hidden){
11756                     this.el.endMeasure();
11757                 }
11758             }
11759         }
11760     },
11761
11762     /**
11763      * Assigns this button's click handler
11764      * @param {Function} handler The function to call when the button is clicked
11765      * @param {Object} scope (optional) Scope for the function passed in
11766      */
11767     setHandler : function(handler, scope){
11768         this.handler = handler;
11769         this.scope = scope;  
11770     },
11771     
11772     /**
11773      * Sets this button's text
11774      * @param {String} text The button text
11775      */
11776     setText : function(text){
11777         this.text = text;
11778         if(this.el){
11779             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11780         }
11781         this.autoWidth();
11782     },
11783     
11784     /**
11785      * Gets the text for this button
11786      * @return {String} The button text
11787      */
11788     getText : function(){
11789         return this.text;  
11790     },
11791     
11792     /**
11793      * Show this button
11794      */
11795     show: function(){
11796         this.hidden = false;
11797         if(this.el){
11798             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11799         }
11800     },
11801     
11802     /**
11803      * Hide this button
11804      */
11805     hide: function(){
11806         this.hidden = true;
11807         if(this.el){
11808             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11809         }
11810     },
11811     
11812     /**
11813      * Convenience function for boolean show/hide
11814      * @param {Boolean} visible True to show, false to hide
11815      */
11816     setVisible: function(visible){
11817         if(visible) {
11818             this.show();
11819         }else{
11820             this.hide();
11821         }
11822     },
11823     
11824     /**
11825      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11826      * @param {Boolean} state (optional) Force a particular state
11827      */
11828     toggle : function(state){
11829         state = state === undefined ? !this.pressed : state;
11830         if(state != this.pressed){
11831             if(state){
11832                 this.el.addClass("x-btn-pressed");
11833                 this.pressed = true;
11834                 this.fireEvent("toggle", this, true);
11835             }else{
11836                 this.el.removeClass("x-btn-pressed");
11837                 this.pressed = false;
11838                 this.fireEvent("toggle", this, false);
11839             }
11840             if(this.toggleHandler){
11841                 this.toggleHandler.call(this.scope || this, this, state);
11842             }
11843         }
11844     },
11845     
11846     /**
11847      * Focus the button
11848      */
11849     focus : function(){
11850         this.el.child('button:first').focus();
11851     },
11852     
11853     /**
11854      * Disable this button
11855      */
11856     disable : function(){
11857         if(this.el){
11858             this.el.addClass("x-btn-disabled");
11859         }
11860         this.disabled = true;
11861     },
11862     
11863     /**
11864      * Enable this button
11865      */
11866     enable : function(){
11867         if(this.el){
11868             this.el.removeClass("x-btn-disabled");
11869         }
11870         this.disabled = false;
11871     },
11872
11873     /**
11874      * Convenience function for boolean enable/disable
11875      * @param {Boolean} enabled True to enable, false to disable
11876      */
11877     setDisabled : function(v){
11878         this[v !== true ? "enable" : "disable"]();
11879     },
11880
11881     // private
11882     onClick : function(e){
11883         if(e){
11884             e.preventDefault();
11885         }
11886         if(e.button != 0){
11887             return;
11888         }
11889         if(!this.disabled){
11890             if(this.enableToggle){
11891                 this.toggle();
11892             }
11893             if(this.menu && !this.menu.isVisible()){
11894                 this.menu.show(this.el, this.menuAlign);
11895             }
11896             this.fireEvent("click", this, e);
11897             if(this.handler){
11898                 this.el.removeClass("x-btn-over");
11899                 this.handler.call(this.scope || this, this, e);
11900             }
11901         }
11902     },
11903     // private
11904     onMouseOver : function(e){
11905         if(!this.disabled){
11906             this.el.addClass("x-btn-over");
11907             this.fireEvent('mouseover', this, e);
11908         }
11909     },
11910     // private
11911     onMouseOut : function(e){
11912         if(!e.within(this.el,  true)){
11913             this.el.removeClass("x-btn-over");
11914             this.fireEvent('mouseout', this, e);
11915         }
11916     },
11917     // private
11918     onFocus : function(e){
11919         if(!this.disabled){
11920             this.el.addClass("x-btn-focus");
11921         }
11922     },
11923     // private
11924     onBlur : function(e){
11925         this.el.removeClass("x-btn-focus");
11926     },
11927     // private
11928     onMouseDown : function(e){
11929         if(!this.disabled && e.button == 0){
11930             this.el.addClass("x-btn-click");
11931             Roo.get(document).on('mouseup', this.onMouseUp, this);
11932         }
11933     },
11934     // private
11935     onMouseUp : function(e){
11936         if(e.button == 0){
11937             this.el.removeClass("x-btn-click");
11938             Roo.get(document).un('mouseup', this.onMouseUp, this);
11939         }
11940     },
11941     // private
11942     onMenuShow : function(e){
11943         this.el.addClass("x-btn-menu-active");
11944     },
11945     // private
11946     onMenuHide : function(e){
11947         this.el.removeClass("x-btn-menu-active");
11948     }   
11949 });
11950
11951 // Private utility class used by Button
11952 Roo.ButtonToggleMgr = function(){
11953    var groups = {};
11954    
11955    function toggleGroup(btn, state){
11956        if(state){
11957            var g = groups[btn.toggleGroup];
11958            for(var i = 0, l = g.length; i < l; i++){
11959                if(g[i] != btn){
11960                    g[i].toggle(false);
11961                }
11962            }
11963        }
11964    }
11965    
11966    return {
11967        register : function(btn){
11968            if(!btn.toggleGroup){
11969                return;
11970            }
11971            var g = groups[btn.toggleGroup];
11972            if(!g){
11973                g = groups[btn.toggleGroup] = [];
11974            }
11975            g.push(btn);
11976            btn.on("toggle", toggleGroup);
11977        },
11978        
11979        unregister : function(btn){
11980            if(!btn.toggleGroup){
11981                return;
11982            }
11983            var g = groups[btn.toggleGroup];
11984            if(g){
11985                g.remove(btn);
11986                btn.un("toggle", toggleGroup);
11987            }
11988        }
11989    };
11990 }();/*
11991  * Based on:
11992  * Ext JS Library 1.1.1
11993  * Copyright(c) 2006-2007, Ext JS, LLC.
11994  *
11995  * Originally Released Under LGPL - original licence link has changed is not relivant.
11996  *
11997  * Fork - LGPL
11998  * <script type="text/javascript">
11999  */
12000  
12001 /**
12002  * @class Roo.SplitButton
12003  * @extends Roo.Button
12004  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12005  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12006  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12007  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12008  * @cfg {String} arrowTooltip The title attribute of the arrow
12009  * @constructor
12010  * Create a new menu button
12011  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12012  * @param {Object} config The config object
12013  */
12014 Roo.SplitButton = function(renderTo, config){
12015     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12016     /**
12017      * @event arrowclick
12018      * Fires when this button's arrow is clicked
12019      * @param {SplitButton} this
12020      * @param {EventObject} e The click event
12021      */
12022     this.addEvents({"arrowclick":true});
12023 };
12024
12025 Roo.extend(Roo.SplitButton, Roo.Button, {
12026     render : function(renderTo){
12027         // this is one sweet looking template!
12028         var tpl = new Roo.Template(
12029             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12030             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12031             '<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>',
12032             "</tbody></table></td><td>",
12033             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12034             '<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>',
12035             "</tbody></table></td></tr></table>"
12036         );
12037         var btn = tpl.append(renderTo, [this.text, this.type], true);
12038         var btnEl = btn.child("button");
12039         if(this.cls){
12040             btn.addClass(this.cls);
12041         }
12042         if(this.icon){
12043             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12044         }
12045         if(this.iconCls){
12046             btnEl.addClass(this.iconCls);
12047             if(!this.cls){
12048                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12049             }
12050         }
12051         this.el = btn;
12052         if(this.handleMouseEvents){
12053             btn.on("mouseover", this.onMouseOver, this);
12054             btn.on("mouseout", this.onMouseOut, this);
12055             btn.on("mousedown", this.onMouseDown, this);
12056             btn.on("mouseup", this.onMouseUp, this);
12057         }
12058         btn.on(this.clickEvent, this.onClick, this);
12059         if(this.tooltip){
12060             if(typeof this.tooltip == 'object'){
12061                 Roo.QuickTips.tips(Roo.apply({
12062                       target: btnEl.id
12063                 }, this.tooltip));
12064             } else {
12065                 btnEl.dom[this.tooltipType] = this.tooltip;
12066             }
12067         }
12068         if(this.arrowTooltip){
12069             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12070         }
12071         if(this.hidden){
12072             this.hide();
12073         }
12074         if(this.disabled){
12075             this.disable();
12076         }
12077         if(this.pressed){
12078             this.el.addClass("x-btn-pressed");
12079         }
12080         if(Roo.isIE && !Roo.isIE7){
12081             this.autoWidth.defer(1, this);
12082         }else{
12083             this.autoWidth();
12084         }
12085         if(this.menu){
12086             this.menu.on("show", this.onMenuShow, this);
12087             this.menu.on("hide", this.onMenuHide, this);
12088         }
12089         this.fireEvent('render', this);
12090     },
12091
12092     // private
12093     autoWidth : function(){
12094         if(this.el){
12095             var tbl = this.el.child("table:first");
12096             var tbl2 = this.el.child("table:last");
12097             this.el.setWidth("auto");
12098             tbl.setWidth("auto");
12099             if(Roo.isIE7 && Roo.isStrict){
12100                 var ib = this.el.child('button:first');
12101                 if(ib && ib.getWidth() > 20){
12102                     ib.clip();
12103                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12104                 }
12105             }
12106             if(this.minWidth){
12107                 if(this.hidden){
12108                     this.el.beginMeasure();
12109                 }
12110                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12111                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12112                 }
12113                 if(this.hidden){
12114                     this.el.endMeasure();
12115                 }
12116             }
12117             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12118         } 
12119     },
12120     /**
12121      * Sets this button's click handler
12122      * @param {Function} handler The function to call when the button is clicked
12123      * @param {Object} scope (optional) Scope for the function passed above
12124      */
12125     setHandler : function(handler, scope){
12126         this.handler = handler;
12127         this.scope = scope;  
12128     },
12129     
12130     /**
12131      * Sets this button's arrow click handler
12132      * @param {Function} handler The function to call when the arrow is clicked
12133      * @param {Object} scope (optional) Scope for the function passed above
12134      */
12135     setArrowHandler : function(handler, scope){
12136         this.arrowHandler = handler;
12137         this.scope = scope;  
12138     },
12139     
12140     /**
12141      * Focus the button
12142      */
12143     focus : function(){
12144         if(this.el){
12145             this.el.child("button:first").focus();
12146         }
12147     },
12148
12149     // private
12150     onClick : function(e){
12151         e.preventDefault();
12152         if(!this.disabled){
12153             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12154                 if(this.menu && !this.menu.isVisible()){
12155                     this.menu.show(this.el, this.menuAlign);
12156                 }
12157                 this.fireEvent("arrowclick", this, e);
12158                 if(this.arrowHandler){
12159                     this.arrowHandler.call(this.scope || this, this, e);
12160                 }
12161             }else{
12162                 this.fireEvent("click", this, e);
12163                 if(this.handler){
12164                     this.handler.call(this.scope || this, this, e);
12165                 }
12166             }
12167         }
12168     },
12169     // private
12170     onMouseDown : function(e){
12171         if(!this.disabled){
12172             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12173         }
12174     },
12175     // private
12176     onMouseUp : function(e){
12177         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12178     }   
12179 });
12180
12181
12182 // backwards compat
12183 Roo.MenuButton = Roo.SplitButton;/*
12184  * Based on:
12185  * Ext JS Library 1.1.1
12186  * Copyright(c) 2006-2007, Ext JS, LLC.
12187  *
12188  * Originally Released Under LGPL - original licence link has changed is not relivant.
12189  *
12190  * Fork - LGPL
12191  * <script type="text/javascript">
12192  */
12193
12194 /**
12195  * @class Roo.Toolbar
12196  * Basic Toolbar class.
12197  * @constructor
12198  * Creates a new Toolbar
12199  * @param {Object} config The config object
12200  */ 
12201 Roo.Toolbar = function(container, buttons, config)
12202 {
12203     /// old consturctor format still supported..
12204     if(container instanceof Array){ // omit the container for later rendering
12205         buttons = container;
12206         config = buttons;
12207         container = null;
12208     }
12209     if (typeof(container) == 'object' && container.xtype) {
12210         config = container;
12211         container = config.container;
12212         buttons = config.buttons; // not really - use items!!
12213     }
12214     var xitems = [];
12215     if (config && config.items) {
12216         xitems = config.items;
12217         delete config.items;
12218     }
12219     Roo.apply(this, config);
12220     this.buttons = buttons;
12221     
12222     if(container){
12223         this.render(container);
12224     }
12225     Roo.each(xitems, function(b) {
12226         this.add(b);
12227     }, this);
12228     
12229 };
12230
12231 Roo.Toolbar.prototype = {
12232     /**
12233      * @cfg {Roo.data.Store} items
12234      * array of button configs or elements to add
12235      */
12236     
12237     /**
12238      * @cfg {String/HTMLElement/Element} container
12239      * The id or element that will contain the toolbar
12240      */
12241     // private
12242     render : function(ct){
12243         this.el = Roo.get(ct);
12244         if(this.cls){
12245             this.el.addClass(this.cls);
12246         }
12247         // using a table allows for vertical alignment
12248         // 100% width is needed by Safari...
12249         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12250         this.tr = this.el.child("tr", true);
12251         var autoId = 0;
12252         this.items = new Roo.util.MixedCollection(false, function(o){
12253             return o.id || ("item" + (++autoId));
12254         });
12255         if(this.buttons){
12256             this.add.apply(this, this.buttons);
12257             delete this.buttons;
12258         }
12259     },
12260
12261     /**
12262      * Adds element(s) to the toolbar -- this function takes a variable number of 
12263      * arguments of mixed type and adds them to the toolbar.
12264      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12265      * <ul>
12266      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12267      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12268      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12269      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12270      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12271      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12272      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12273      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12274      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12275      * </ul>
12276      * @param {Mixed} arg2
12277      * @param {Mixed} etc.
12278      */
12279     add : function(){
12280         var a = arguments, l = a.length;
12281         for(var i = 0; i < l; i++){
12282             this._add(a[i]);
12283         }
12284     },
12285     // private..
12286     _add : function(el) {
12287         
12288         if (el.xtype) {
12289             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12290         }
12291         
12292         if (el.applyTo){ // some kind of form field
12293             return this.addField(el);
12294         } 
12295         if (el.render){ // some kind of Toolbar.Item
12296             return this.addItem(el);
12297         }
12298         if (typeof el == "string"){ // string
12299             if(el == "separator" || el == "-"){
12300                 return this.addSeparator();
12301             }
12302             if (el == " "){
12303                 return this.addSpacer();
12304             }
12305             if(el == "->"){
12306                 return this.addFill();
12307             }
12308             return this.addText(el);
12309             
12310         }
12311         if(el.tagName){ // element
12312             return this.addElement(el);
12313         }
12314         if(typeof el == "object"){ // must be button config?
12315             return this.addButton(el);
12316         }
12317         // and now what?!?!
12318         return false;
12319         
12320     },
12321     
12322     /**
12323      * Add an Xtype element
12324      * @param {Object} xtype Xtype Object
12325      * @return {Object} created Object
12326      */
12327     addxtype : function(e){
12328         return this.add(e);  
12329     },
12330     
12331     /**
12332      * Returns the Element for this toolbar.
12333      * @return {Roo.Element}
12334      */
12335     getEl : function(){
12336         return this.el;  
12337     },
12338     
12339     /**
12340      * Adds a separator
12341      * @return {Roo.Toolbar.Item} The separator item
12342      */
12343     addSeparator : function(){
12344         return this.addItem(new Roo.Toolbar.Separator());
12345     },
12346
12347     /**
12348      * Adds a spacer element
12349      * @return {Roo.Toolbar.Spacer} The spacer item
12350      */
12351     addSpacer : function(){
12352         return this.addItem(new Roo.Toolbar.Spacer());
12353     },
12354
12355     /**
12356      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12357      * @return {Roo.Toolbar.Fill} The fill item
12358      */
12359     addFill : function(){
12360         return this.addItem(new Roo.Toolbar.Fill());
12361     },
12362
12363     /**
12364      * Adds any standard HTML element to the toolbar
12365      * @param {String/HTMLElement/Element} el The element or id of the element to add
12366      * @return {Roo.Toolbar.Item} The element's item
12367      */
12368     addElement : function(el){
12369         return this.addItem(new Roo.Toolbar.Item(el));
12370     },
12371     /**
12372      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12373      * @type Roo.util.MixedCollection  
12374      */
12375     items : false,
12376      
12377     /**
12378      * Adds any Toolbar.Item or subclass
12379      * @param {Roo.Toolbar.Item} item
12380      * @return {Roo.Toolbar.Item} The item
12381      */
12382     addItem : function(item){
12383         var td = this.nextBlock();
12384         item.render(td);
12385         this.items.add(item);
12386         return item;
12387     },
12388     
12389     /**
12390      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12391      * @param {Object/Array} config A button config or array of configs
12392      * @return {Roo.Toolbar.Button/Array}
12393      */
12394     addButton : function(config){
12395         if(config instanceof Array){
12396             var buttons = [];
12397             for(var i = 0, len = config.length; i < len; i++) {
12398                 buttons.push(this.addButton(config[i]));
12399             }
12400             return buttons;
12401         }
12402         var b = config;
12403         if(!(config instanceof Roo.Toolbar.Button)){
12404             b = config.split ?
12405                 new Roo.Toolbar.SplitButton(config) :
12406                 new Roo.Toolbar.Button(config);
12407         }
12408         var td = this.nextBlock();
12409         b.render(td);
12410         this.items.add(b);
12411         return b;
12412     },
12413     
12414     /**
12415      * Adds text to the toolbar
12416      * @param {String} text The text to add
12417      * @return {Roo.Toolbar.Item} The element's item
12418      */
12419     addText : function(text){
12420         return this.addItem(new Roo.Toolbar.TextItem(text));
12421     },
12422     
12423     /**
12424      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12425      * @param {Number} index The index where the item is to be inserted
12426      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12427      * @return {Roo.Toolbar.Button/Item}
12428      */
12429     insertButton : function(index, item){
12430         if(item instanceof Array){
12431             var buttons = [];
12432             for(var i = 0, len = item.length; i < len; i++) {
12433                buttons.push(this.insertButton(index + i, item[i]));
12434             }
12435             return buttons;
12436         }
12437         if (!(item instanceof Roo.Toolbar.Button)){
12438            item = new Roo.Toolbar.Button(item);
12439         }
12440         var td = document.createElement("td");
12441         this.tr.insertBefore(td, this.tr.childNodes[index]);
12442         item.render(td);
12443         this.items.insert(index, item);
12444         return item;
12445     },
12446     
12447     /**
12448      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12449      * @param {Object} config
12450      * @return {Roo.Toolbar.Item} The element's item
12451      */
12452     addDom : function(config, returnEl){
12453         var td = this.nextBlock();
12454         Roo.DomHelper.overwrite(td, config);
12455         var ti = new Roo.Toolbar.Item(td.firstChild);
12456         ti.render(td);
12457         this.items.add(ti);
12458         return ti;
12459     },
12460
12461     /**
12462      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12463      * @type Roo.util.MixedCollection  
12464      */
12465     fields : false,
12466     
12467     /**
12468      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12469      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12470      * @param {Roo.form.Field} field
12471      * @return {Roo.ToolbarItem}
12472      */
12473      
12474       
12475     addField : function(field) {
12476         if (!this.fields) {
12477             var autoId = 0;
12478             this.fields = new Roo.util.MixedCollection(false, function(o){
12479                 return o.id || ("item" + (++autoId));
12480             });
12481
12482         }
12483         
12484         var td = this.nextBlock();
12485         field.render(td);
12486         var ti = new Roo.Toolbar.Item(td.firstChild);
12487         ti.render(td);
12488         this.items.add(ti);
12489         this.fields.add(field);
12490         return ti;
12491     },
12492     /**
12493      * Hide the toolbar
12494      * @method hide
12495      */
12496      
12497       
12498     hide : function()
12499     {
12500         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12501         this.el.child('div').hide();
12502     },
12503     /**
12504      * Show the toolbar
12505      * @method show
12506      */
12507     show : function()
12508     {
12509         this.el.child('div').show();
12510     },
12511       
12512     // private
12513     nextBlock : function(){
12514         var td = document.createElement("td");
12515         this.tr.appendChild(td);
12516         return td;
12517     },
12518
12519     // private
12520     destroy : function(){
12521         if(this.items){ // rendered?
12522             Roo.destroy.apply(Roo, this.items.items);
12523         }
12524         if(this.fields){ // rendered?
12525             Roo.destroy.apply(Roo, this.fields.items);
12526         }
12527         Roo.Element.uncache(this.el, this.tr);
12528     }
12529 };
12530
12531 /**
12532  * @class Roo.Toolbar.Item
12533  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12534  * @constructor
12535  * Creates a new Item
12536  * @param {HTMLElement} el 
12537  */
12538 Roo.Toolbar.Item = function(el){
12539     this.el = Roo.getDom(el);
12540     this.id = Roo.id(this.el);
12541     this.hidden = false;
12542 };
12543
12544 Roo.Toolbar.Item.prototype = {
12545     
12546     /**
12547      * Get this item's HTML Element
12548      * @return {HTMLElement}
12549      */
12550     getEl : function(){
12551        return this.el;  
12552     },
12553
12554     // private
12555     render : function(td){
12556         this.td = td;
12557         td.appendChild(this.el);
12558     },
12559     
12560     /**
12561      * Removes and destroys this item.
12562      */
12563     destroy : function(){
12564         this.td.parentNode.removeChild(this.td);
12565     },
12566     
12567     /**
12568      * Shows this item.
12569      */
12570     show: function(){
12571         this.hidden = false;
12572         this.td.style.display = "";
12573     },
12574     
12575     /**
12576      * Hides this item.
12577      */
12578     hide: function(){
12579         this.hidden = true;
12580         this.td.style.display = "none";
12581     },
12582     
12583     /**
12584      * Convenience function for boolean show/hide.
12585      * @param {Boolean} visible true to show/false to hide
12586      */
12587     setVisible: function(visible){
12588         if(visible) {
12589             this.show();
12590         }else{
12591             this.hide();
12592         }
12593     },
12594     
12595     /**
12596      * Try to focus this item.
12597      */
12598     focus : function(){
12599         Roo.fly(this.el).focus();
12600     },
12601     
12602     /**
12603      * Disables this item.
12604      */
12605     disable : function(){
12606         Roo.fly(this.td).addClass("x-item-disabled");
12607         this.disabled = true;
12608         this.el.disabled = true;
12609     },
12610     
12611     /**
12612      * Enables this item.
12613      */
12614     enable : function(){
12615         Roo.fly(this.td).removeClass("x-item-disabled");
12616         this.disabled = false;
12617         this.el.disabled = false;
12618     }
12619 };
12620
12621
12622 /**
12623  * @class Roo.Toolbar.Separator
12624  * @extends Roo.Toolbar.Item
12625  * A simple toolbar separator class
12626  * @constructor
12627  * Creates a new Separator
12628  */
12629 Roo.Toolbar.Separator = function(){
12630     var s = document.createElement("span");
12631     s.className = "ytb-sep";
12632     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12633 };
12634 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12635     enable:Roo.emptyFn,
12636     disable:Roo.emptyFn,
12637     focus:Roo.emptyFn
12638 });
12639
12640 /**
12641  * @class Roo.Toolbar.Spacer
12642  * @extends Roo.Toolbar.Item
12643  * A simple element that adds extra horizontal space to a toolbar.
12644  * @constructor
12645  * Creates a new Spacer
12646  */
12647 Roo.Toolbar.Spacer = function(){
12648     var s = document.createElement("div");
12649     s.className = "ytb-spacer";
12650     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12651 };
12652 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12653     enable:Roo.emptyFn,
12654     disable:Roo.emptyFn,
12655     focus:Roo.emptyFn
12656 });
12657
12658 /**
12659  * @class Roo.Toolbar.Fill
12660  * @extends Roo.Toolbar.Spacer
12661  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12662  * @constructor
12663  * Creates a new Spacer
12664  */
12665 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12666     // private
12667     render : function(td){
12668         td.style.width = '100%';
12669         Roo.Toolbar.Fill.superclass.render.call(this, td);
12670     }
12671 });
12672
12673 /**
12674  * @class Roo.Toolbar.TextItem
12675  * @extends Roo.Toolbar.Item
12676  * A simple class that renders text directly into a toolbar.
12677  * @constructor
12678  * Creates a new TextItem
12679  * @param {String} text
12680  */
12681 Roo.Toolbar.TextItem = function(text){
12682     if (typeof(text) == 'object') {
12683         text = text.text;
12684     }
12685     var s = document.createElement("span");
12686     s.className = "ytb-text";
12687     s.innerHTML = text;
12688     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12689 };
12690 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12691     enable:Roo.emptyFn,
12692     disable:Roo.emptyFn,
12693     focus:Roo.emptyFn
12694 });
12695
12696 /**
12697  * @class Roo.Toolbar.Button
12698  * @extends Roo.Button
12699  * A button that renders into a toolbar.
12700  * @constructor
12701  * Creates a new Button
12702  * @param {Object} config A standard {@link Roo.Button} config object
12703  */
12704 Roo.Toolbar.Button = function(config){
12705     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12706 };
12707 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12708     render : function(td){
12709         this.td = td;
12710         Roo.Toolbar.Button.superclass.render.call(this, td);
12711     },
12712     
12713     /**
12714      * Removes and destroys this button
12715      */
12716     destroy : function(){
12717         Roo.Toolbar.Button.superclass.destroy.call(this);
12718         this.td.parentNode.removeChild(this.td);
12719     },
12720     
12721     /**
12722      * Shows this button
12723      */
12724     show: function(){
12725         this.hidden = false;
12726         this.td.style.display = "";
12727     },
12728     
12729     /**
12730      * Hides this button
12731      */
12732     hide: function(){
12733         this.hidden = true;
12734         this.td.style.display = "none";
12735     },
12736
12737     /**
12738      * Disables this item
12739      */
12740     disable : function(){
12741         Roo.fly(this.td).addClass("x-item-disabled");
12742         this.disabled = true;
12743     },
12744
12745     /**
12746      * Enables this item
12747      */
12748     enable : function(){
12749         Roo.fly(this.td).removeClass("x-item-disabled");
12750         this.disabled = false;
12751     }
12752 });
12753 // backwards compat
12754 Roo.ToolbarButton = Roo.Toolbar.Button;
12755
12756 /**
12757  * @class Roo.Toolbar.SplitButton
12758  * @extends Roo.SplitButton
12759  * A menu button that renders into a toolbar.
12760  * @constructor
12761  * Creates a new SplitButton
12762  * @param {Object} config A standard {@link Roo.SplitButton} config object
12763  */
12764 Roo.Toolbar.SplitButton = function(config){
12765     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12766 };
12767 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12768     render : function(td){
12769         this.td = td;
12770         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12771     },
12772     
12773     /**
12774      * Removes and destroys this button
12775      */
12776     destroy : function(){
12777         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12778         this.td.parentNode.removeChild(this.td);
12779     },
12780     
12781     /**
12782      * Shows this button
12783      */
12784     show: function(){
12785         this.hidden = false;
12786         this.td.style.display = "";
12787     },
12788     
12789     /**
12790      * Hides this button
12791      */
12792     hide: function(){
12793         this.hidden = true;
12794         this.td.style.display = "none";
12795     }
12796 });
12797
12798 // backwards compat
12799 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12800  * Based on:
12801  * Ext JS Library 1.1.1
12802  * Copyright(c) 2006-2007, Ext JS, LLC.
12803  *
12804  * Originally Released Under LGPL - original licence link has changed is not relivant.
12805  *
12806  * Fork - LGPL
12807  * <script type="text/javascript">
12808  */
12809  
12810 /**
12811  * @class Roo.PagingToolbar
12812  * @extends Roo.Toolbar
12813  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12814  * @constructor
12815  * Create a new PagingToolbar
12816  * @param {Object} config The config object
12817  */
12818 Roo.PagingToolbar = function(el, ds, config)
12819 {
12820     // old args format still supported... - xtype is prefered..
12821     if (typeof(el) == 'object' && el.xtype) {
12822         // created from xtype...
12823         config = el;
12824         ds = el.dataSource;
12825         el = config.container;
12826     }
12827     var items = [];
12828     if (config.items) {
12829         items = config.items;
12830         config.items = [];
12831     }
12832     
12833     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12834     this.ds = ds;
12835     this.cursor = 0;
12836     this.renderButtons(this.el);
12837     this.bind(ds);
12838     
12839     // supprot items array.
12840    
12841     Roo.each(items, function(e) {
12842         this.add(Roo.factory(e));
12843     },this);
12844     
12845 };
12846
12847 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12848     /**
12849      * @cfg {Roo.data.Store} dataSource
12850      * The underlying data store providing the paged data
12851      */
12852     /**
12853      * @cfg {String/HTMLElement/Element} container
12854      * container The id or element that will contain the toolbar
12855      */
12856     /**
12857      * @cfg {Boolean} displayInfo
12858      * True to display the displayMsg (defaults to false)
12859      */
12860     /**
12861      * @cfg {Number} pageSize
12862      * The number of records to display per page (defaults to 20)
12863      */
12864     pageSize: 20,
12865     /**
12866      * @cfg {String} displayMsg
12867      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12868      */
12869     displayMsg : 'Displaying {0} - {1} of {2}',
12870     /**
12871      * @cfg {String} emptyMsg
12872      * The message to display when no records are found (defaults to "No data to display")
12873      */
12874     emptyMsg : 'No data to display',
12875     /**
12876      * Customizable piece of the default paging text (defaults to "Page")
12877      * @type String
12878      */
12879     beforePageText : "Page",
12880     /**
12881      * Customizable piece of the default paging text (defaults to "of %0")
12882      * @type String
12883      */
12884     afterPageText : "of {0}",
12885     /**
12886      * Customizable piece of the default paging text (defaults to "First Page")
12887      * @type String
12888      */
12889     firstText : "First Page",
12890     /**
12891      * Customizable piece of the default paging text (defaults to "Previous Page")
12892      * @type String
12893      */
12894     prevText : "Previous Page",
12895     /**
12896      * Customizable piece of the default paging text (defaults to "Next Page")
12897      * @type String
12898      */
12899     nextText : "Next Page",
12900     /**
12901      * Customizable piece of the default paging text (defaults to "Last Page")
12902      * @type String
12903      */
12904     lastText : "Last Page",
12905     /**
12906      * Customizable piece of the default paging text (defaults to "Refresh")
12907      * @type String
12908      */
12909     refreshText : "Refresh",
12910
12911     // private
12912     renderButtons : function(el){
12913         Roo.PagingToolbar.superclass.render.call(this, el);
12914         this.first = this.addButton({
12915             tooltip: this.firstText,
12916             cls: "x-btn-icon x-grid-page-first",
12917             disabled: true,
12918             handler: this.onClick.createDelegate(this, ["first"])
12919         });
12920         this.prev = this.addButton({
12921             tooltip: this.prevText,
12922             cls: "x-btn-icon x-grid-page-prev",
12923             disabled: true,
12924             handler: this.onClick.createDelegate(this, ["prev"])
12925         });
12926         //this.addSeparator();
12927         this.add(this.beforePageText);
12928         this.field = Roo.get(this.addDom({
12929            tag: "input",
12930            type: "text",
12931            size: "3",
12932            value: "1",
12933            cls: "x-grid-page-number"
12934         }).el);
12935         this.field.on("keydown", this.onPagingKeydown, this);
12936         this.field.on("focus", function(){this.dom.select();});
12937         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12938         this.field.setHeight(18);
12939         //this.addSeparator();
12940         this.next = this.addButton({
12941             tooltip: this.nextText,
12942             cls: "x-btn-icon x-grid-page-next",
12943             disabled: true,
12944             handler: this.onClick.createDelegate(this, ["next"])
12945         });
12946         this.last = this.addButton({
12947             tooltip: this.lastText,
12948             cls: "x-btn-icon x-grid-page-last",
12949             disabled: true,
12950             handler: this.onClick.createDelegate(this, ["last"])
12951         });
12952         //this.addSeparator();
12953         this.loading = this.addButton({
12954             tooltip: this.refreshText,
12955             cls: "x-btn-icon x-grid-loading",
12956             handler: this.onClick.createDelegate(this, ["refresh"])
12957         });
12958
12959         if(this.displayInfo){
12960             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12961         }
12962     },
12963
12964     // private
12965     updateInfo : function(){
12966         if(this.displayEl){
12967             var count = this.ds.getCount();
12968             var msg = count == 0 ?
12969                 this.emptyMsg :
12970                 String.format(
12971                     this.displayMsg,
12972                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12973                 );
12974             this.displayEl.update(msg);
12975         }
12976     },
12977
12978     // private
12979     onLoad : function(ds, r, o){
12980        this.cursor = o.params ? o.params.start : 0;
12981        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12982
12983        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12984        this.field.dom.value = ap;
12985        this.first.setDisabled(ap == 1);
12986        this.prev.setDisabled(ap == 1);
12987        this.next.setDisabled(ap == ps);
12988        this.last.setDisabled(ap == ps);
12989        this.loading.enable();
12990        this.updateInfo();
12991     },
12992
12993     // private
12994     getPageData : function(){
12995         var total = this.ds.getTotalCount();
12996         return {
12997             total : total,
12998             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12999             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13000         };
13001     },
13002
13003     // private
13004     onLoadError : function(){
13005         this.loading.enable();
13006     },
13007
13008     // private
13009     onPagingKeydown : function(e){
13010         var k = e.getKey();
13011         var d = this.getPageData();
13012         if(k == e.RETURN){
13013             var v = this.field.dom.value, pageNum;
13014             if(!v || isNaN(pageNum = parseInt(v, 10))){
13015                 this.field.dom.value = d.activePage;
13016                 return;
13017             }
13018             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13019             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13020             e.stopEvent();
13021         }
13022         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))
13023         {
13024           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13025           this.field.dom.value = pageNum;
13026           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13027           e.stopEvent();
13028         }
13029         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13030         {
13031           var v = this.field.dom.value, pageNum; 
13032           var increment = (e.shiftKey) ? 10 : 1;
13033           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13034             increment *= -1;
13035           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13036             this.field.dom.value = d.activePage;
13037             return;
13038           }
13039           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13040           {
13041             this.field.dom.value = parseInt(v, 10) + increment;
13042             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13043             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13044           }
13045           e.stopEvent();
13046         }
13047     },
13048
13049     // private
13050     beforeLoad : function(){
13051         if(this.loading){
13052             this.loading.disable();
13053         }
13054     },
13055
13056     // private
13057     onClick : function(which){
13058         var ds = this.ds;
13059         switch(which){
13060             case "first":
13061                 ds.load({params:{start: 0, limit: this.pageSize}});
13062             break;
13063             case "prev":
13064                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13065             break;
13066             case "next":
13067                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13068             break;
13069             case "last":
13070                 var total = ds.getTotalCount();
13071                 var extra = total % this.pageSize;
13072                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13073                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13074             break;
13075             case "refresh":
13076                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13077             break;
13078         }
13079     },
13080
13081     /**
13082      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13083      * @param {Roo.data.Store} store The data store to unbind
13084      */
13085     unbind : function(ds){
13086         ds.un("beforeload", this.beforeLoad, this);
13087         ds.un("load", this.onLoad, this);
13088         ds.un("loadexception", this.onLoadError, this);
13089         ds.un("remove", this.updateInfo, this);
13090         ds.un("add", this.updateInfo, this);
13091         this.ds = undefined;
13092     },
13093
13094     /**
13095      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13096      * @param {Roo.data.Store} store The data store to bind
13097      */
13098     bind : function(ds){
13099         ds.on("beforeload", this.beforeLoad, this);
13100         ds.on("load", this.onLoad, this);
13101         ds.on("loadexception", this.onLoadError, this);
13102         ds.on("remove", this.updateInfo, this);
13103         ds.on("add", this.updateInfo, this);
13104         this.ds = ds;
13105     }
13106 });/*
13107  * Based on:
13108  * Ext JS Library 1.1.1
13109  * Copyright(c) 2006-2007, Ext JS, LLC.
13110  *
13111  * Originally Released Under LGPL - original licence link has changed is not relivant.
13112  *
13113  * Fork - LGPL
13114  * <script type="text/javascript">
13115  */
13116
13117 /**
13118  * @class Roo.Resizable
13119  * @extends Roo.util.Observable
13120  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13121  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13122  * 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
13123  * the element will be wrapped for you automatically.</p>
13124  * <p>Here is the list of valid resize handles:</p>
13125  * <pre>
13126 Value   Description
13127 ------  -------------------
13128  'n'     north
13129  's'     south
13130  'e'     east
13131  'w'     west
13132  'nw'    northwest
13133  'sw'    southwest
13134  'se'    southeast
13135  'ne'    northeast
13136  'hd'    horizontal drag
13137  'all'   all
13138 </pre>
13139  * <p>Here's an example showing the creation of a typical Resizable:</p>
13140  * <pre><code>
13141 var resizer = new Roo.Resizable("element-id", {
13142     handles: 'all',
13143     minWidth: 200,
13144     minHeight: 100,
13145     maxWidth: 500,
13146     maxHeight: 400,
13147     pinned: true
13148 });
13149 resizer.on("resize", myHandler);
13150 </code></pre>
13151  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13152  * resizer.east.setDisplayed(false);</p>
13153  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13154  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13155  * resize operation's new size (defaults to [0, 0])
13156  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13157  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13158  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13159  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13160  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13161  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13162  * @cfg {Number} width The width of the element in pixels (defaults to null)
13163  * @cfg {Number} height The height of the element in pixels (defaults to null)
13164  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13165  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13166  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13167  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13168  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13169  * in favor of the handles config option (defaults to false)
13170  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13171  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13172  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13173  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13174  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13175  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13176  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13177  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13178  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13179  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13180  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13181  * @constructor
13182  * Create a new resizable component
13183  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13184  * @param {Object} config configuration options
13185   */
13186 Roo.Resizable = function(el, config)
13187 {
13188     this.el = Roo.get(el);
13189
13190     if(config && config.wrap){
13191         config.resizeChild = this.el;
13192         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13193         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13194         this.el.setStyle("overflow", "hidden");
13195         this.el.setPositioning(config.resizeChild.getPositioning());
13196         config.resizeChild.clearPositioning();
13197         if(!config.width || !config.height){
13198             var csize = config.resizeChild.getSize();
13199             this.el.setSize(csize.width, csize.height);
13200         }
13201         if(config.pinned && !config.adjustments){
13202             config.adjustments = "auto";
13203         }
13204     }
13205
13206     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13207     this.proxy.unselectable();
13208     this.proxy.enableDisplayMode('block');
13209
13210     Roo.apply(this, config);
13211
13212     if(this.pinned){
13213         this.disableTrackOver = true;
13214         this.el.addClass("x-resizable-pinned");
13215     }
13216     // if the element isn't positioned, make it relative
13217     var position = this.el.getStyle("position");
13218     if(position != "absolute" && position != "fixed"){
13219         this.el.setStyle("position", "relative");
13220     }
13221     if(!this.handles){ // no handles passed, must be legacy style
13222         this.handles = 's,e,se';
13223         if(this.multiDirectional){
13224             this.handles += ',n,w';
13225         }
13226     }
13227     if(this.handles == "all"){
13228         this.handles = "n s e w ne nw se sw";
13229     }
13230     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13231     var ps = Roo.Resizable.positions;
13232     for(var i = 0, len = hs.length; i < len; i++){
13233         if(hs[i] && ps[hs[i]]){
13234             var pos = ps[hs[i]];
13235             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13236         }
13237     }
13238     // legacy
13239     this.corner = this.southeast;
13240     
13241     // updateBox = the box can move..
13242     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13243         this.updateBox = true;
13244     }
13245
13246     this.activeHandle = null;
13247
13248     if(this.resizeChild){
13249         if(typeof this.resizeChild == "boolean"){
13250             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13251         }else{
13252             this.resizeChild = Roo.get(this.resizeChild, true);
13253         }
13254     }
13255     
13256     if(this.adjustments == "auto"){
13257         var rc = this.resizeChild;
13258         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13259         if(rc && (hw || hn)){
13260             rc.position("relative");
13261             rc.setLeft(hw ? hw.el.getWidth() : 0);
13262             rc.setTop(hn ? hn.el.getHeight() : 0);
13263         }
13264         this.adjustments = [
13265             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13266             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13267         ];
13268     }
13269
13270     if(this.draggable){
13271         this.dd = this.dynamic ?
13272             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13273         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13274     }
13275
13276     // public events
13277     this.addEvents({
13278         /**
13279          * @event beforeresize
13280          * Fired before resize is allowed. Set enabled to false to cancel resize.
13281          * @param {Roo.Resizable} this
13282          * @param {Roo.EventObject} e The mousedown event
13283          */
13284         "beforeresize" : true,
13285         /**
13286          * @event resize
13287          * Fired after a resize.
13288          * @param {Roo.Resizable} this
13289          * @param {Number} width The new width
13290          * @param {Number} height The new height
13291          * @param {Roo.EventObject} e The mouseup event
13292          */
13293         "resize" : true
13294     });
13295
13296     if(this.width !== null && this.height !== null){
13297         this.resizeTo(this.width, this.height);
13298     }else{
13299         this.updateChildSize();
13300     }
13301     if(Roo.isIE){
13302         this.el.dom.style.zoom = 1;
13303     }
13304     Roo.Resizable.superclass.constructor.call(this);
13305 };
13306
13307 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13308         resizeChild : false,
13309         adjustments : [0, 0],
13310         minWidth : 5,
13311         minHeight : 5,
13312         maxWidth : 10000,
13313         maxHeight : 10000,
13314         enabled : true,
13315         animate : false,
13316         duration : .35,
13317         dynamic : false,
13318         handles : false,
13319         multiDirectional : false,
13320         disableTrackOver : false,
13321         easing : 'easeOutStrong',
13322         widthIncrement : 0,
13323         heightIncrement : 0,
13324         pinned : false,
13325         width : null,
13326         height : null,
13327         preserveRatio : false,
13328         transparent: false,
13329         minX: 0,
13330         minY: 0,
13331         draggable: false,
13332
13333         /**
13334          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13335          */
13336         constrainTo: undefined,
13337         /**
13338          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13339          */
13340         resizeRegion: undefined,
13341
13342
13343     /**
13344      * Perform a manual resize
13345      * @param {Number} width
13346      * @param {Number} height
13347      */
13348     resizeTo : function(width, height){
13349         this.el.setSize(width, height);
13350         this.updateChildSize();
13351         this.fireEvent("resize", this, width, height, null);
13352     },
13353
13354     // private
13355     startSizing : function(e, handle){
13356         this.fireEvent("beforeresize", this, e);
13357         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13358
13359             if(!this.overlay){
13360                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13361                 this.overlay.unselectable();
13362                 this.overlay.enableDisplayMode("block");
13363                 this.overlay.on("mousemove", this.onMouseMove, this);
13364                 this.overlay.on("mouseup", this.onMouseUp, this);
13365             }
13366             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13367
13368             this.resizing = true;
13369             this.startBox = this.el.getBox();
13370             this.startPoint = e.getXY();
13371             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13372                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13373
13374             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13375             this.overlay.show();
13376
13377             if(this.constrainTo) {
13378                 var ct = Roo.get(this.constrainTo);
13379                 this.resizeRegion = ct.getRegion().adjust(
13380                     ct.getFrameWidth('t'),
13381                     ct.getFrameWidth('l'),
13382                     -ct.getFrameWidth('b'),
13383                     -ct.getFrameWidth('r')
13384                 );
13385             }
13386
13387             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13388             this.proxy.show();
13389             this.proxy.setBox(this.startBox);
13390             if(!this.dynamic){
13391                 this.proxy.setStyle('visibility', 'visible');
13392             }
13393         }
13394     },
13395
13396     // private
13397     onMouseDown : function(handle, e){
13398         if(this.enabled){
13399             e.stopEvent();
13400             this.activeHandle = handle;
13401             this.startSizing(e, handle);
13402         }
13403     },
13404
13405     // private
13406     onMouseUp : function(e){
13407         var size = this.resizeElement();
13408         this.resizing = false;
13409         this.handleOut();
13410         this.overlay.hide();
13411         this.proxy.hide();
13412         this.fireEvent("resize", this, size.width, size.height, e);
13413     },
13414
13415     // private
13416     updateChildSize : function(){
13417         if(this.resizeChild){
13418             var el = this.el;
13419             var child = this.resizeChild;
13420             var adj = this.adjustments;
13421             if(el.dom.offsetWidth){
13422                 var b = el.getSize(true);
13423                 child.setSize(b.width+adj[0], b.height+adj[1]);
13424             }
13425             // Second call here for IE
13426             // The first call enables instant resizing and
13427             // the second call corrects scroll bars if they
13428             // exist
13429             if(Roo.isIE){
13430                 setTimeout(function(){
13431                     if(el.dom.offsetWidth){
13432                         var b = el.getSize(true);
13433                         child.setSize(b.width+adj[0], b.height+adj[1]);
13434                     }
13435                 }, 10);
13436             }
13437         }
13438     },
13439
13440     // private
13441     snap : function(value, inc, min){
13442         if(!inc || !value) return value;
13443         var newValue = value;
13444         var m = value % inc;
13445         if(m > 0){
13446             if(m > (inc/2)){
13447                 newValue = value + (inc-m);
13448             }else{
13449                 newValue = value - m;
13450             }
13451         }
13452         return Math.max(min, newValue);
13453     },
13454
13455     // private
13456     resizeElement : function(){
13457         var box = this.proxy.getBox();
13458         if(this.updateBox){
13459             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13460         }else{
13461             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13462         }
13463         this.updateChildSize();
13464         if(!this.dynamic){
13465             this.proxy.hide();
13466         }
13467         return box;
13468     },
13469
13470     // private
13471     constrain : function(v, diff, m, mx){
13472         if(v - diff < m){
13473             diff = v - m;
13474         }else if(v - diff > mx){
13475             diff = mx - v;
13476         }
13477         return diff;
13478     },
13479
13480     // private
13481     onMouseMove : function(e){
13482         if(this.enabled){
13483             try{// try catch so if something goes wrong the user doesn't get hung
13484
13485             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13486                 return;
13487             }
13488
13489             //var curXY = this.startPoint;
13490             var curSize = this.curSize || this.startBox;
13491             var x = this.startBox.x, y = this.startBox.y;
13492             var ox = x, oy = y;
13493             var w = curSize.width, h = curSize.height;
13494             var ow = w, oh = h;
13495             var mw = this.minWidth, mh = this.minHeight;
13496             var mxw = this.maxWidth, mxh = this.maxHeight;
13497             var wi = this.widthIncrement;
13498             var hi = this.heightIncrement;
13499
13500             var eventXY = e.getXY();
13501             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13502             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13503
13504             var pos = this.activeHandle.position;
13505
13506             switch(pos){
13507                 case "east":
13508                     w += diffX;
13509                     w = Math.min(Math.max(mw, w), mxw);
13510                     break;
13511              
13512                 case "south":
13513                     h += diffY;
13514                     h = Math.min(Math.max(mh, h), mxh);
13515                     break;
13516                 case "southeast":
13517                     w += diffX;
13518                     h += diffY;
13519                     w = Math.min(Math.max(mw, w), mxw);
13520                     h = Math.min(Math.max(mh, h), mxh);
13521                     break;
13522                 case "north":
13523                     diffY = this.constrain(h, diffY, mh, mxh);
13524                     y += diffY;
13525                     h -= diffY;
13526                     break;
13527                 case "hdrag":
13528                     
13529                     if (wi) {
13530                         var adiffX = Math.abs(diffX);
13531                         var sub = (adiffX % wi); // how much 
13532                         if (sub > (wi/2)) { // far enough to snap
13533                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13534                         } else {
13535                             // remove difference.. 
13536                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13537                         }
13538                     }
13539                     x += diffX;
13540                     x = Math.max(this.minX, x);
13541                     break;
13542                 case "west":
13543                     diffX = this.constrain(w, diffX, mw, mxw);
13544                     x += diffX;
13545                     w -= diffX;
13546                     break;
13547                 case "northeast":
13548                     w += diffX;
13549                     w = Math.min(Math.max(mw, w), mxw);
13550                     diffY = this.constrain(h, diffY, mh, mxh);
13551                     y += diffY;
13552                     h -= diffY;
13553                     break;
13554                 case "northwest":
13555                     diffX = this.constrain(w, diffX, mw, mxw);
13556                     diffY = this.constrain(h, diffY, mh, mxh);
13557                     y += diffY;
13558                     h -= diffY;
13559                     x += diffX;
13560                     w -= diffX;
13561                     break;
13562                case "southwest":
13563                     diffX = this.constrain(w, diffX, mw, mxw);
13564                     h += diffY;
13565                     h = Math.min(Math.max(mh, h), mxh);
13566                     x += diffX;
13567                     w -= diffX;
13568                     break;
13569             }
13570
13571             var sw = this.snap(w, wi, mw);
13572             var sh = this.snap(h, hi, mh);
13573             if(sw != w || sh != h){
13574                 switch(pos){
13575                     case "northeast":
13576                         y -= sh - h;
13577                     break;
13578                     case "north":
13579                         y -= sh - h;
13580                         break;
13581                     case "southwest":
13582                         x -= sw - w;
13583                     break;
13584                     case "west":
13585                         x -= sw - w;
13586                         break;
13587                     case "northwest":
13588                         x -= sw - w;
13589                         y -= sh - h;
13590                     break;
13591                 }
13592                 w = sw;
13593                 h = sh;
13594             }
13595
13596             if(this.preserveRatio){
13597                 switch(pos){
13598                     case "southeast":
13599                     case "east":
13600                         h = oh * (w/ow);
13601                         h = Math.min(Math.max(mh, h), mxh);
13602                         w = ow * (h/oh);
13603                        break;
13604                     case "south":
13605                         w = ow * (h/oh);
13606                         w = Math.min(Math.max(mw, w), mxw);
13607                         h = oh * (w/ow);
13608                         break;
13609                     case "northeast":
13610                         w = ow * (h/oh);
13611                         w = Math.min(Math.max(mw, w), mxw);
13612                         h = oh * (w/ow);
13613                     break;
13614                     case "north":
13615                         var tw = w;
13616                         w = ow * (h/oh);
13617                         w = Math.min(Math.max(mw, w), mxw);
13618                         h = oh * (w/ow);
13619                         x += (tw - w) / 2;
13620                         break;
13621                     case "southwest":
13622                         h = oh * (w/ow);
13623                         h = Math.min(Math.max(mh, h), mxh);
13624                         var tw = w;
13625                         w = ow * (h/oh);
13626                         x += tw - w;
13627                         break;
13628                     case "west":
13629                         var th = h;
13630                         h = oh * (w/ow);
13631                         h = Math.min(Math.max(mh, h), mxh);
13632                         y += (th - h) / 2;
13633                         var tw = w;
13634                         w = ow * (h/oh);
13635                         x += tw - w;
13636                        break;
13637                     case "northwest":
13638                         var tw = w;
13639                         var th = h;
13640                         h = oh * (w/ow);
13641                         h = Math.min(Math.max(mh, h), mxh);
13642                         w = ow * (h/oh);
13643                         y += th - h;
13644                         x += tw - w;
13645                        break;
13646
13647                 }
13648             }
13649             if (pos == 'hdrag') {
13650                 w = ow;
13651             }
13652             this.proxy.setBounds(x, y, w, h);
13653             if(this.dynamic){
13654                 this.resizeElement();
13655             }
13656             }catch(e){}
13657         }
13658     },
13659
13660     // private
13661     handleOver : function(){
13662         if(this.enabled){
13663             this.el.addClass("x-resizable-over");
13664         }
13665     },
13666
13667     // private
13668     handleOut : function(){
13669         if(!this.resizing){
13670             this.el.removeClass("x-resizable-over");
13671         }
13672     },
13673
13674     /**
13675      * Returns the element this component is bound to.
13676      * @return {Roo.Element}
13677      */
13678     getEl : function(){
13679         return this.el;
13680     },
13681
13682     /**
13683      * Returns the resizeChild element (or null).
13684      * @return {Roo.Element}
13685      */
13686     getResizeChild : function(){
13687         return this.resizeChild;
13688     },
13689
13690     /**
13691      * Destroys this resizable. If the element was wrapped and
13692      * removeEl is not true then the element remains.
13693      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13694      */
13695     destroy : function(removeEl){
13696         this.proxy.remove();
13697         if(this.overlay){
13698             this.overlay.removeAllListeners();
13699             this.overlay.remove();
13700         }
13701         var ps = Roo.Resizable.positions;
13702         for(var k in ps){
13703             if(typeof ps[k] != "function" && this[ps[k]]){
13704                 var h = this[ps[k]];
13705                 h.el.removeAllListeners();
13706                 h.el.remove();
13707             }
13708         }
13709         if(removeEl){
13710             this.el.update("");
13711             this.el.remove();
13712         }
13713     }
13714 });
13715
13716 // private
13717 // hash to map config positions to true positions
13718 Roo.Resizable.positions = {
13719     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13720     hd: "hdrag"
13721 };
13722
13723 // private
13724 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13725     if(!this.tpl){
13726         // only initialize the template if resizable is used
13727         var tpl = Roo.DomHelper.createTemplate(
13728             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13729         );
13730         tpl.compile();
13731         Roo.Resizable.Handle.prototype.tpl = tpl;
13732     }
13733     this.position = pos;
13734     this.rz = rz;
13735     // show north drag fro topdra
13736     var handlepos = pos == 'hdrag' ? 'north' : pos;
13737     
13738     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13739     if (pos == 'hdrag') {
13740         this.el.setStyle('cursor', 'pointer');
13741     }
13742     this.el.unselectable();
13743     if(transparent){
13744         this.el.setOpacity(0);
13745     }
13746     this.el.on("mousedown", this.onMouseDown, this);
13747     if(!disableTrackOver){
13748         this.el.on("mouseover", this.onMouseOver, this);
13749         this.el.on("mouseout", this.onMouseOut, this);
13750     }
13751 };
13752
13753 // private
13754 Roo.Resizable.Handle.prototype = {
13755     afterResize : function(rz){
13756         // do nothing
13757     },
13758     // private
13759     onMouseDown : function(e){
13760         this.rz.onMouseDown(this, e);
13761     },
13762     // private
13763     onMouseOver : function(e){
13764         this.rz.handleOver(this, e);
13765     },
13766     // private
13767     onMouseOut : function(e){
13768         this.rz.handleOut(this, e);
13769     }
13770 };/*
13771  * Based on:
13772  * Ext JS Library 1.1.1
13773  * Copyright(c) 2006-2007, Ext JS, LLC.
13774  *
13775  * Originally Released Under LGPL - original licence link has changed is not relivant.
13776  *
13777  * Fork - LGPL
13778  * <script type="text/javascript">
13779  */
13780
13781 /**
13782  * @class Roo.Editor
13783  * @extends Roo.Component
13784  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13785  * @constructor
13786  * Create a new Editor
13787  * @param {Roo.form.Field} field The Field object (or descendant)
13788  * @param {Object} config The config object
13789  */
13790 Roo.Editor = function(field, config){
13791     Roo.Editor.superclass.constructor.call(this, config);
13792     this.field = field;
13793     this.addEvents({
13794         /**
13795              * @event beforestartedit
13796              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13797              * false from the handler of this event.
13798              * @param {Editor} this
13799              * @param {Roo.Element} boundEl The underlying element bound to this editor
13800              * @param {Mixed} value The field value being set
13801              */
13802         "beforestartedit" : true,
13803         /**
13804              * @event startedit
13805              * Fires when this editor is displayed
13806              * @param {Roo.Element} boundEl The underlying element bound to this editor
13807              * @param {Mixed} value The starting field value
13808              */
13809         "startedit" : true,
13810         /**
13811              * @event beforecomplete
13812              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13813              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13814              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13815              * event will not fire since no edit actually occurred.
13816              * @param {Editor} this
13817              * @param {Mixed} value The current field value
13818              * @param {Mixed} startValue The original field value
13819              */
13820         "beforecomplete" : true,
13821         /**
13822              * @event complete
13823              * Fires after editing is complete and any changed value has been written to the underlying field.
13824              * @param {Editor} this
13825              * @param {Mixed} value The current field value
13826              * @param {Mixed} startValue The original field value
13827              */
13828         "complete" : true,
13829         /**
13830          * @event specialkey
13831          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13832          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13833          * @param {Roo.form.Field} this
13834          * @param {Roo.EventObject} e The event object
13835          */
13836         "specialkey" : true
13837     });
13838 };
13839
13840 Roo.extend(Roo.Editor, Roo.Component, {
13841     /**
13842      * @cfg {Boolean/String} autosize
13843      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13844      * or "height" to adopt the height only (defaults to false)
13845      */
13846     /**
13847      * @cfg {Boolean} revertInvalid
13848      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13849      * validation fails (defaults to true)
13850      */
13851     /**
13852      * @cfg {Boolean} ignoreNoChange
13853      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13854      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13855      * will never be ignored.
13856      */
13857     /**
13858      * @cfg {Boolean} hideEl
13859      * False to keep the bound element visible while the editor is displayed (defaults to true)
13860      */
13861     /**
13862      * @cfg {Mixed} value
13863      * The data value of the underlying field (defaults to "")
13864      */
13865     value : "",
13866     /**
13867      * @cfg {String} alignment
13868      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13869      */
13870     alignment: "c-c?",
13871     /**
13872      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13873      * for bottom-right shadow (defaults to "frame")
13874      */
13875     shadow : "frame",
13876     /**
13877      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13878      */
13879     constrain : false,
13880     /**
13881      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13882      */
13883     completeOnEnter : false,
13884     /**
13885      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13886      */
13887     cancelOnEsc : false,
13888     /**
13889      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13890      */
13891     updateEl : false,
13892
13893     // private
13894     onRender : function(ct, position){
13895         this.el = new Roo.Layer({
13896             shadow: this.shadow,
13897             cls: "x-editor",
13898             parentEl : ct,
13899             shim : this.shim,
13900             shadowOffset:4,
13901             id: this.id,
13902             constrain: this.constrain
13903         });
13904         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13905         if(this.field.msgTarget != 'title'){
13906             this.field.msgTarget = 'qtip';
13907         }
13908         this.field.render(this.el);
13909         if(Roo.isGecko){
13910             this.field.el.dom.setAttribute('autocomplete', 'off');
13911         }
13912         this.field.on("specialkey", this.onSpecialKey, this);
13913         if(this.swallowKeys){
13914             this.field.el.swallowEvent(['keydown','keypress']);
13915         }
13916         this.field.show();
13917         this.field.on("blur", this.onBlur, this);
13918         if(this.field.grow){
13919             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13920         }
13921     },
13922
13923     onSpecialKey : function(field, e)
13924     {
13925         //Roo.log('editor onSpecialKey');
13926         if(this.completeOnEnter && e.getKey() == e.ENTER){
13927             e.stopEvent();
13928             this.completeEdit();
13929             return;
13930         }
13931         // do not fire special key otherwise it might hide close the editor...
13932         if(e.getKey() == e.ENTER){    
13933             return;
13934         }
13935         if(this.cancelOnEsc && e.getKey() == e.ESC){
13936             this.cancelEdit();
13937             return;
13938         } 
13939         this.fireEvent('specialkey', field, e);
13940     
13941     },
13942
13943     /**
13944      * Starts the editing process and shows the editor.
13945      * @param {String/HTMLElement/Element} el The element to edit
13946      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13947       * to the innerHTML of el.
13948      */
13949     startEdit : function(el, value){
13950         if(this.editing){
13951             this.completeEdit();
13952         }
13953         this.boundEl = Roo.get(el);
13954         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13955         if(!this.rendered){
13956             this.render(this.parentEl || document.body);
13957         }
13958         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13959             return;
13960         }
13961         this.startValue = v;
13962         this.field.setValue(v);
13963         if(this.autoSize){
13964             var sz = this.boundEl.getSize();
13965             switch(this.autoSize){
13966                 case "width":
13967                 this.setSize(sz.width,  "");
13968                 break;
13969                 case "height":
13970                 this.setSize("",  sz.height);
13971                 break;
13972                 default:
13973                 this.setSize(sz.width,  sz.height);
13974             }
13975         }
13976         this.el.alignTo(this.boundEl, this.alignment);
13977         this.editing = true;
13978         if(Roo.QuickTips){
13979             Roo.QuickTips.disable();
13980         }
13981         this.show();
13982     },
13983
13984     /**
13985      * Sets the height and width of this editor.
13986      * @param {Number} width The new width
13987      * @param {Number} height The new height
13988      */
13989     setSize : function(w, h){
13990         this.field.setSize(w, h);
13991         if(this.el){
13992             this.el.sync();
13993         }
13994     },
13995
13996     /**
13997      * Realigns the editor to the bound field based on the current alignment config value.
13998      */
13999     realign : function(){
14000         this.el.alignTo(this.boundEl, this.alignment);
14001     },
14002
14003     /**
14004      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14005      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14006      */
14007     completeEdit : function(remainVisible){
14008         if(!this.editing){
14009             return;
14010         }
14011         var v = this.getValue();
14012         if(this.revertInvalid !== false && !this.field.isValid()){
14013             v = this.startValue;
14014             this.cancelEdit(true);
14015         }
14016         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14017             this.editing = false;
14018             this.hide();
14019             return;
14020         }
14021         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14022             this.editing = false;
14023             if(this.updateEl && this.boundEl){
14024                 this.boundEl.update(v);
14025             }
14026             if(remainVisible !== true){
14027                 this.hide();
14028             }
14029             this.fireEvent("complete", this, v, this.startValue);
14030         }
14031     },
14032
14033     // private
14034     onShow : function(){
14035         this.el.show();
14036         if(this.hideEl !== false){
14037             this.boundEl.hide();
14038         }
14039         this.field.show();
14040         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14041             this.fixIEFocus = true;
14042             this.deferredFocus.defer(50, this);
14043         }else{
14044             this.field.focus();
14045         }
14046         this.fireEvent("startedit", this.boundEl, this.startValue);
14047     },
14048
14049     deferredFocus : function(){
14050         if(this.editing){
14051             this.field.focus();
14052         }
14053     },
14054
14055     /**
14056      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14057      * reverted to the original starting value.
14058      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14059      * cancel (defaults to false)
14060      */
14061     cancelEdit : function(remainVisible){
14062         if(this.editing){
14063             this.setValue(this.startValue);
14064             if(remainVisible !== true){
14065                 this.hide();
14066             }
14067         }
14068     },
14069
14070     // private
14071     onBlur : function(){
14072         if(this.allowBlur !== true && this.editing){
14073             this.completeEdit();
14074         }
14075     },
14076
14077     // private
14078     onHide : function(){
14079         if(this.editing){
14080             this.completeEdit();
14081             return;
14082         }
14083         this.field.blur();
14084         if(this.field.collapse){
14085             this.field.collapse();
14086         }
14087         this.el.hide();
14088         if(this.hideEl !== false){
14089             this.boundEl.show();
14090         }
14091         if(Roo.QuickTips){
14092             Roo.QuickTips.enable();
14093         }
14094     },
14095
14096     /**
14097      * Sets the data value of the editor
14098      * @param {Mixed} value Any valid value supported by the underlying field
14099      */
14100     setValue : function(v){
14101         this.field.setValue(v);
14102     },
14103
14104     /**
14105      * Gets the data value of the editor
14106      * @return {Mixed} The data value
14107      */
14108     getValue : function(){
14109         return this.field.getValue();
14110     }
14111 });/*
14112  * Based on:
14113  * Ext JS Library 1.1.1
14114  * Copyright(c) 2006-2007, Ext JS, LLC.
14115  *
14116  * Originally Released Under LGPL - original licence link has changed is not relivant.
14117  *
14118  * Fork - LGPL
14119  * <script type="text/javascript">
14120  */
14121  
14122 /**
14123  * @class Roo.BasicDialog
14124  * @extends Roo.util.Observable
14125  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14126  * <pre><code>
14127 var dlg = new Roo.BasicDialog("my-dlg", {
14128     height: 200,
14129     width: 300,
14130     minHeight: 100,
14131     minWidth: 150,
14132     modal: true,
14133     proxyDrag: true,
14134     shadow: true
14135 });
14136 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14137 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14138 dlg.addButton('Cancel', dlg.hide, dlg);
14139 dlg.show();
14140 </code></pre>
14141   <b>A Dialog should always be a direct child of the body element.</b>
14142  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14143  * @cfg {String} title Default text to display in the title bar (defaults to null)
14144  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14145  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14146  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14147  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14148  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14149  * (defaults to null with no animation)
14150  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14151  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14152  * property for valid values (defaults to 'all')
14153  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14154  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14155  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14156  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14157  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14158  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14159  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14160  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14161  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14162  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14163  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14164  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14165  * draggable = true (defaults to false)
14166  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14167  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14168  * shadow (defaults to false)
14169  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14170  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14171  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14172  * @cfg {Array} buttons Array of buttons
14173  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14174  * @constructor
14175  * Create a new BasicDialog.
14176  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14177  * @param {Object} config Configuration options
14178  */
14179 Roo.BasicDialog = function(el, config){
14180     this.el = Roo.get(el);
14181     var dh = Roo.DomHelper;
14182     if(!this.el && config && config.autoCreate){
14183         if(typeof config.autoCreate == "object"){
14184             if(!config.autoCreate.id){
14185                 config.autoCreate.id = el;
14186             }
14187             this.el = dh.append(document.body,
14188                         config.autoCreate, true);
14189         }else{
14190             this.el = dh.append(document.body,
14191                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14192         }
14193     }
14194     el = this.el;
14195     el.setDisplayed(true);
14196     el.hide = this.hideAction;
14197     this.id = el.id;
14198     el.addClass("x-dlg");
14199
14200     Roo.apply(this, config);
14201
14202     this.proxy = el.createProxy("x-dlg-proxy");
14203     this.proxy.hide = this.hideAction;
14204     this.proxy.setOpacity(.5);
14205     this.proxy.hide();
14206
14207     if(config.width){
14208         el.setWidth(config.width);
14209     }
14210     if(config.height){
14211         el.setHeight(config.height);
14212     }
14213     this.size = el.getSize();
14214     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14215         this.xy = [config.x,config.y];
14216     }else{
14217         this.xy = el.getCenterXY(true);
14218     }
14219     /** The header element @type Roo.Element */
14220     this.header = el.child("> .x-dlg-hd");
14221     /** The body element @type Roo.Element */
14222     this.body = el.child("> .x-dlg-bd");
14223     /** The footer element @type Roo.Element */
14224     this.footer = el.child("> .x-dlg-ft");
14225
14226     if(!this.header){
14227         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14228     }
14229     if(!this.body){
14230         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14231     }
14232
14233     this.header.unselectable();
14234     if(this.title){
14235         this.header.update(this.title);
14236     }
14237     // this element allows the dialog to be focused for keyboard event
14238     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14239     this.focusEl.swallowEvent("click", true);
14240
14241     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14242
14243     // wrap the body and footer for special rendering
14244     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14245     if(this.footer){
14246         this.bwrap.dom.appendChild(this.footer.dom);
14247     }
14248
14249     this.bg = this.el.createChild({
14250         tag: "div", cls:"x-dlg-bg",
14251         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14252     });
14253     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14254
14255
14256     if(this.autoScroll !== false && !this.autoTabs){
14257         this.body.setStyle("overflow", "auto");
14258     }
14259
14260     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14261
14262     if(this.closable !== false){
14263         this.el.addClass("x-dlg-closable");
14264         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14265         this.close.on("click", this.closeClick, this);
14266         this.close.addClassOnOver("x-dlg-close-over");
14267     }
14268     if(this.collapsible !== false){
14269         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14270         this.collapseBtn.on("click", this.collapseClick, this);
14271         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14272         this.header.on("dblclick", this.collapseClick, this);
14273     }
14274     if(this.resizable !== false){
14275         this.el.addClass("x-dlg-resizable");
14276         this.resizer = new Roo.Resizable(el, {
14277             minWidth: this.minWidth || 80,
14278             minHeight:this.minHeight || 80,
14279             handles: this.resizeHandles || "all",
14280             pinned: true
14281         });
14282         this.resizer.on("beforeresize", this.beforeResize, this);
14283         this.resizer.on("resize", this.onResize, this);
14284     }
14285     if(this.draggable !== false){
14286         el.addClass("x-dlg-draggable");
14287         if (!this.proxyDrag) {
14288             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14289         }
14290         else {
14291             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14292         }
14293         dd.setHandleElId(this.header.id);
14294         dd.endDrag = this.endMove.createDelegate(this);
14295         dd.startDrag = this.startMove.createDelegate(this);
14296         dd.onDrag = this.onDrag.createDelegate(this);
14297         dd.scroll = false;
14298         this.dd = dd;
14299     }
14300     if(this.modal){
14301         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14302         this.mask.enableDisplayMode("block");
14303         this.mask.hide();
14304         this.el.addClass("x-dlg-modal");
14305     }
14306     if(this.shadow){
14307         this.shadow = new Roo.Shadow({
14308             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14309             offset : this.shadowOffset
14310         });
14311     }else{
14312         this.shadowOffset = 0;
14313     }
14314     if(Roo.useShims && this.shim !== false){
14315         this.shim = this.el.createShim();
14316         this.shim.hide = this.hideAction;
14317         this.shim.hide();
14318     }else{
14319         this.shim = false;
14320     }
14321     if(this.autoTabs){
14322         this.initTabs();
14323     }
14324     if (this.buttons) { 
14325         var bts= this.buttons;
14326         this.buttons = [];
14327         Roo.each(bts, function(b) {
14328             this.addButton(b);
14329         }, this);
14330     }
14331     
14332     
14333     this.addEvents({
14334         /**
14335          * @event keydown
14336          * Fires when a key is pressed
14337          * @param {Roo.BasicDialog} this
14338          * @param {Roo.EventObject} e
14339          */
14340         "keydown" : true,
14341         /**
14342          * @event move
14343          * Fires when this dialog is moved by the user.
14344          * @param {Roo.BasicDialog} this
14345          * @param {Number} x The new page X
14346          * @param {Number} y The new page Y
14347          */
14348         "move" : true,
14349         /**
14350          * @event resize
14351          * Fires when this dialog is resized by the user.
14352          * @param {Roo.BasicDialog} this
14353          * @param {Number} width The new width
14354          * @param {Number} height The new height
14355          */
14356         "resize" : true,
14357         /**
14358          * @event beforehide
14359          * Fires before this dialog is hidden.
14360          * @param {Roo.BasicDialog} this
14361          */
14362         "beforehide" : true,
14363         /**
14364          * @event hide
14365          * Fires when this dialog is hidden.
14366          * @param {Roo.BasicDialog} this
14367          */
14368         "hide" : true,
14369         /**
14370          * @event beforeshow
14371          * Fires before this dialog is shown.
14372          * @param {Roo.BasicDialog} this
14373          */
14374         "beforeshow" : true,
14375         /**
14376          * @event show
14377          * Fires when this dialog is shown.
14378          * @param {Roo.BasicDialog} this
14379          */
14380         "show" : true
14381     });
14382     el.on("keydown", this.onKeyDown, this);
14383     el.on("mousedown", this.toFront, this);
14384     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14385     this.el.hide();
14386     Roo.DialogManager.register(this);
14387     Roo.BasicDialog.superclass.constructor.call(this);
14388 };
14389
14390 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14391     shadowOffset: Roo.isIE ? 6 : 5,
14392     minHeight: 80,
14393     minWidth: 200,
14394     minButtonWidth: 75,
14395     defaultButton: null,
14396     buttonAlign: "right",
14397     tabTag: 'div',
14398     firstShow: true,
14399
14400     /**
14401      * Sets the dialog title text
14402      * @param {String} text The title text to display
14403      * @return {Roo.BasicDialog} this
14404      */
14405     setTitle : function(text){
14406         this.header.update(text);
14407         return this;
14408     },
14409
14410     // private
14411     closeClick : function(){
14412         this.hide();
14413     },
14414
14415     // private
14416     collapseClick : function(){
14417         this[this.collapsed ? "expand" : "collapse"]();
14418     },
14419
14420     /**
14421      * Collapses the dialog to its minimized state (only the title bar is visible).
14422      * Equivalent to the user clicking the collapse dialog button.
14423      */
14424     collapse : function(){
14425         if(!this.collapsed){
14426             this.collapsed = true;
14427             this.el.addClass("x-dlg-collapsed");
14428             this.restoreHeight = this.el.getHeight();
14429             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14430         }
14431     },
14432
14433     /**
14434      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14435      * clicking the expand dialog button.
14436      */
14437     expand : function(){
14438         if(this.collapsed){
14439             this.collapsed = false;
14440             this.el.removeClass("x-dlg-collapsed");
14441             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14442         }
14443     },
14444
14445     /**
14446      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14447      * @return {Roo.TabPanel} The tabs component
14448      */
14449     initTabs : function(){
14450         var tabs = this.getTabs();
14451         while(tabs.getTab(0)){
14452             tabs.removeTab(0);
14453         }
14454         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14455             var dom = el.dom;
14456             tabs.addTab(Roo.id(dom), dom.title);
14457             dom.title = "";
14458         });
14459         tabs.activate(0);
14460         return tabs;
14461     },
14462
14463     // private
14464     beforeResize : function(){
14465         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14466     },
14467
14468     // private
14469     onResize : function(){
14470         this.refreshSize();
14471         this.syncBodyHeight();
14472         this.adjustAssets();
14473         this.focus();
14474         this.fireEvent("resize", this, this.size.width, this.size.height);
14475     },
14476
14477     // private
14478     onKeyDown : function(e){
14479         if(this.isVisible()){
14480             this.fireEvent("keydown", this, e);
14481         }
14482     },
14483
14484     /**
14485      * Resizes the dialog.
14486      * @param {Number} width
14487      * @param {Number} height
14488      * @return {Roo.BasicDialog} this
14489      */
14490     resizeTo : function(width, height){
14491         this.el.setSize(width, height);
14492         this.size = {width: width, height: height};
14493         this.syncBodyHeight();
14494         if(this.fixedcenter){
14495             this.center();
14496         }
14497         if(this.isVisible()){
14498             this.constrainXY();
14499             this.adjustAssets();
14500         }
14501         this.fireEvent("resize", this, width, height);
14502         return this;
14503     },
14504
14505
14506     /**
14507      * Resizes the dialog to fit the specified content size.
14508      * @param {Number} width
14509      * @param {Number} height
14510      * @return {Roo.BasicDialog} this
14511      */
14512     setContentSize : function(w, h){
14513         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14514         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14515         //if(!this.el.isBorderBox()){
14516             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14517             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14518         //}
14519         if(this.tabs){
14520             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14521             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14522         }
14523         this.resizeTo(w, h);
14524         return this;
14525     },
14526
14527     /**
14528      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14529      * executed in response to a particular key being pressed while the dialog is active.
14530      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14531      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14532      * @param {Function} fn The function to call
14533      * @param {Object} scope (optional) The scope of the function
14534      * @return {Roo.BasicDialog} this
14535      */
14536     addKeyListener : function(key, fn, scope){
14537         var keyCode, shift, ctrl, alt;
14538         if(typeof key == "object" && !(key instanceof Array)){
14539             keyCode = key["key"];
14540             shift = key["shift"];
14541             ctrl = key["ctrl"];
14542             alt = key["alt"];
14543         }else{
14544             keyCode = key;
14545         }
14546         var handler = function(dlg, e){
14547             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14548                 var k = e.getKey();
14549                 if(keyCode instanceof Array){
14550                     for(var i = 0, len = keyCode.length; i < len; i++){
14551                         if(keyCode[i] == k){
14552                           fn.call(scope || window, dlg, k, e);
14553                           return;
14554                         }
14555                     }
14556                 }else{
14557                     if(k == keyCode){
14558                         fn.call(scope || window, dlg, k, e);
14559                     }
14560                 }
14561             }
14562         };
14563         this.on("keydown", handler);
14564         return this;
14565     },
14566
14567     /**
14568      * Returns the TabPanel component (creates it if it doesn't exist).
14569      * Note: If you wish to simply check for the existence of tabs without creating them,
14570      * check for a null 'tabs' property.
14571      * @return {Roo.TabPanel} The tabs component
14572      */
14573     getTabs : function(){
14574         if(!this.tabs){
14575             this.el.addClass("x-dlg-auto-tabs");
14576             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14577             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14578         }
14579         return this.tabs;
14580     },
14581
14582     /**
14583      * Adds a button to the footer section of the dialog.
14584      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14585      * object or a valid Roo.DomHelper element config
14586      * @param {Function} handler The function called when the button is clicked
14587      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14588      * @return {Roo.Button} The new button
14589      */
14590     addButton : function(config, handler, scope){
14591         var dh = Roo.DomHelper;
14592         if(!this.footer){
14593             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14594         }
14595         if(!this.btnContainer){
14596             var tb = this.footer.createChild({
14597
14598                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14599                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14600             }, null, true);
14601             this.btnContainer = tb.firstChild.firstChild.firstChild;
14602         }
14603         var bconfig = {
14604             handler: handler,
14605             scope: scope,
14606             minWidth: this.minButtonWidth,
14607             hideParent:true
14608         };
14609         if(typeof config == "string"){
14610             bconfig.text = config;
14611         }else{
14612             if(config.tag){
14613                 bconfig.dhconfig = config;
14614             }else{
14615                 Roo.apply(bconfig, config);
14616             }
14617         }
14618         var fc = false;
14619         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14620             bconfig.position = Math.max(0, bconfig.position);
14621             fc = this.btnContainer.childNodes[bconfig.position];
14622         }
14623          
14624         var btn = new Roo.Button(
14625             fc ? 
14626                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14627                 : this.btnContainer.appendChild(document.createElement("td")),
14628             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14629             bconfig
14630         );
14631         this.syncBodyHeight();
14632         if(!this.buttons){
14633             /**
14634              * Array of all the buttons that have been added to this dialog via addButton
14635              * @type Array
14636              */
14637             this.buttons = [];
14638         }
14639         this.buttons.push(btn);
14640         return btn;
14641     },
14642
14643     /**
14644      * Sets the default button to be focused when the dialog is displayed.
14645      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14646      * @return {Roo.BasicDialog} this
14647      */
14648     setDefaultButton : function(btn){
14649         this.defaultButton = btn;
14650         return this;
14651     },
14652
14653     // private
14654     getHeaderFooterHeight : function(safe){
14655         var height = 0;
14656         if(this.header){
14657            height += this.header.getHeight();
14658         }
14659         if(this.footer){
14660            var fm = this.footer.getMargins();
14661             height += (this.footer.getHeight()+fm.top+fm.bottom);
14662         }
14663         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14664         height += this.centerBg.getPadding("tb");
14665         return height;
14666     },
14667
14668     // private
14669     syncBodyHeight : function(){
14670         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14671         var height = this.size.height - this.getHeaderFooterHeight(false);
14672         bd.setHeight(height-bd.getMargins("tb"));
14673         var hh = this.header.getHeight();
14674         var h = this.size.height-hh;
14675         cb.setHeight(h);
14676         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14677         bw.setHeight(h-cb.getPadding("tb"));
14678         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14679         bd.setWidth(bw.getWidth(true));
14680         if(this.tabs){
14681             this.tabs.syncHeight();
14682             if(Roo.isIE){
14683                 this.tabs.el.repaint();
14684             }
14685         }
14686     },
14687
14688     /**
14689      * Restores the previous state of the dialog if Roo.state is configured.
14690      * @return {Roo.BasicDialog} this
14691      */
14692     restoreState : function(){
14693         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14694         if(box && box.width){
14695             this.xy = [box.x, box.y];
14696             this.resizeTo(box.width, box.height);
14697         }
14698         return this;
14699     },
14700
14701     // private
14702     beforeShow : function(){
14703         this.expand();
14704         if(this.fixedcenter){
14705             this.xy = this.el.getCenterXY(true);
14706         }
14707         if(this.modal){
14708             Roo.get(document.body).addClass("x-body-masked");
14709             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14710             this.mask.show();
14711         }
14712         this.constrainXY();
14713     },
14714
14715     // private
14716     animShow : function(){
14717         var b = Roo.get(this.animateTarget).getBox();
14718         this.proxy.setSize(b.width, b.height);
14719         this.proxy.setLocation(b.x, b.y);
14720         this.proxy.show();
14721         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14722                     true, .35, this.showEl.createDelegate(this));
14723     },
14724
14725     /**
14726      * Shows the dialog.
14727      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14728      * @return {Roo.BasicDialog} this
14729      */
14730     show : function(animateTarget){
14731         if (this.fireEvent("beforeshow", this) === false){
14732             return;
14733         }
14734         if(this.syncHeightBeforeShow){
14735             this.syncBodyHeight();
14736         }else if(this.firstShow){
14737             this.firstShow = false;
14738             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14739         }
14740         this.animateTarget = animateTarget || this.animateTarget;
14741         if(!this.el.isVisible()){
14742             this.beforeShow();
14743             if(this.animateTarget && Roo.get(this.animateTarget)){
14744                 this.animShow();
14745             }else{
14746                 this.showEl();
14747             }
14748         }
14749         return this;
14750     },
14751
14752     // private
14753     showEl : function(){
14754         this.proxy.hide();
14755         this.el.setXY(this.xy);
14756         this.el.show();
14757         this.adjustAssets(true);
14758         this.toFront();
14759         this.focus();
14760         // IE peekaboo bug - fix found by Dave Fenwick
14761         if(Roo.isIE){
14762             this.el.repaint();
14763         }
14764         this.fireEvent("show", this);
14765     },
14766
14767     /**
14768      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14769      * dialog itself will receive focus.
14770      */
14771     focus : function(){
14772         if(this.defaultButton){
14773             this.defaultButton.focus();
14774         }else{
14775             this.focusEl.focus();
14776         }
14777     },
14778
14779     // private
14780     constrainXY : function(){
14781         if(this.constraintoviewport !== false){
14782             if(!this.viewSize){
14783                 if(this.container){
14784                     var s = this.container.getSize();
14785                     this.viewSize = [s.width, s.height];
14786                 }else{
14787                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14788                 }
14789             }
14790             var s = Roo.get(this.container||document).getScroll();
14791
14792             var x = this.xy[0], y = this.xy[1];
14793             var w = this.size.width, h = this.size.height;
14794             var vw = this.viewSize[0], vh = this.viewSize[1];
14795             // only move it if it needs it
14796             var moved = false;
14797             // first validate right/bottom
14798             if(x + w > vw+s.left){
14799                 x = vw - w;
14800                 moved = true;
14801             }
14802             if(y + h > vh+s.top){
14803                 y = vh - h;
14804                 moved = true;
14805             }
14806             // then make sure top/left isn't negative
14807             if(x < s.left){
14808                 x = s.left;
14809                 moved = true;
14810             }
14811             if(y < s.top){
14812                 y = s.top;
14813                 moved = true;
14814             }
14815             if(moved){
14816                 // cache xy
14817                 this.xy = [x, y];
14818                 if(this.isVisible()){
14819                     this.el.setLocation(x, y);
14820                     this.adjustAssets();
14821                 }
14822             }
14823         }
14824     },
14825
14826     // private
14827     onDrag : function(){
14828         if(!this.proxyDrag){
14829             this.xy = this.el.getXY();
14830             this.adjustAssets();
14831         }
14832     },
14833
14834     // private
14835     adjustAssets : function(doShow){
14836         var x = this.xy[0], y = this.xy[1];
14837         var w = this.size.width, h = this.size.height;
14838         if(doShow === true){
14839             if(this.shadow){
14840                 this.shadow.show(this.el);
14841             }
14842             if(this.shim){
14843                 this.shim.show();
14844             }
14845         }
14846         if(this.shadow && this.shadow.isVisible()){
14847             this.shadow.show(this.el);
14848         }
14849         if(this.shim && this.shim.isVisible()){
14850             this.shim.setBounds(x, y, w, h);
14851         }
14852     },
14853
14854     // private
14855     adjustViewport : function(w, h){
14856         if(!w || !h){
14857             w = Roo.lib.Dom.getViewWidth();
14858             h = Roo.lib.Dom.getViewHeight();
14859         }
14860         // cache the size
14861         this.viewSize = [w, h];
14862         if(this.modal && this.mask.isVisible()){
14863             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14864             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14865         }
14866         if(this.isVisible()){
14867             this.constrainXY();
14868         }
14869     },
14870
14871     /**
14872      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14873      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14874      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14875      */
14876     destroy : function(removeEl){
14877         if(this.isVisible()){
14878             this.animateTarget = null;
14879             this.hide();
14880         }
14881         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14882         if(this.tabs){
14883             this.tabs.destroy(removeEl);
14884         }
14885         Roo.destroy(
14886              this.shim,
14887              this.proxy,
14888              this.resizer,
14889              this.close,
14890              this.mask
14891         );
14892         if(this.dd){
14893             this.dd.unreg();
14894         }
14895         if(this.buttons){
14896            for(var i = 0, len = this.buttons.length; i < len; i++){
14897                this.buttons[i].destroy();
14898            }
14899         }
14900         this.el.removeAllListeners();
14901         if(removeEl === true){
14902             this.el.update("");
14903             this.el.remove();
14904         }
14905         Roo.DialogManager.unregister(this);
14906     },
14907
14908     // private
14909     startMove : function(){
14910         if(this.proxyDrag){
14911             this.proxy.show();
14912         }
14913         if(this.constraintoviewport !== false){
14914             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14915         }
14916     },
14917
14918     // private
14919     endMove : function(){
14920         if(!this.proxyDrag){
14921             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14922         }else{
14923             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14924             this.proxy.hide();
14925         }
14926         this.refreshSize();
14927         this.adjustAssets();
14928         this.focus();
14929         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14930     },
14931
14932     /**
14933      * Brings this dialog to the front of any other visible dialogs
14934      * @return {Roo.BasicDialog} this
14935      */
14936     toFront : function(){
14937         Roo.DialogManager.bringToFront(this);
14938         return this;
14939     },
14940
14941     /**
14942      * Sends this dialog to the back (under) of any other visible dialogs
14943      * @return {Roo.BasicDialog} this
14944      */
14945     toBack : function(){
14946         Roo.DialogManager.sendToBack(this);
14947         return this;
14948     },
14949
14950     /**
14951      * Centers this dialog in the viewport
14952      * @return {Roo.BasicDialog} this
14953      */
14954     center : function(){
14955         var xy = this.el.getCenterXY(true);
14956         this.moveTo(xy[0], xy[1]);
14957         return this;
14958     },
14959
14960     /**
14961      * Moves the dialog's top-left corner to the specified point
14962      * @param {Number} x
14963      * @param {Number} y
14964      * @return {Roo.BasicDialog} this
14965      */
14966     moveTo : function(x, y){
14967         this.xy = [x,y];
14968         if(this.isVisible()){
14969             this.el.setXY(this.xy);
14970             this.adjustAssets();
14971         }
14972         return this;
14973     },
14974
14975     /**
14976      * Aligns the dialog to the specified element
14977      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14978      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14979      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14980      * @return {Roo.BasicDialog} this
14981      */
14982     alignTo : function(element, position, offsets){
14983         this.xy = this.el.getAlignToXY(element, position, offsets);
14984         if(this.isVisible()){
14985             this.el.setXY(this.xy);
14986             this.adjustAssets();
14987         }
14988         return this;
14989     },
14990
14991     /**
14992      * Anchors an element to another element and realigns it when the window is resized.
14993      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14994      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14995      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14996      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14997      * is a number, it is used as the buffer delay (defaults to 50ms).
14998      * @return {Roo.BasicDialog} this
14999      */
15000     anchorTo : function(el, alignment, offsets, monitorScroll){
15001         var action = function(){
15002             this.alignTo(el, alignment, offsets);
15003         };
15004         Roo.EventManager.onWindowResize(action, this);
15005         var tm = typeof monitorScroll;
15006         if(tm != 'undefined'){
15007             Roo.EventManager.on(window, 'scroll', action, this,
15008                 {buffer: tm == 'number' ? monitorScroll : 50});
15009         }
15010         action.call(this);
15011         return this;
15012     },
15013
15014     /**
15015      * Returns true if the dialog is visible
15016      * @return {Boolean}
15017      */
15018     isVisible : function(){
15019         return this.el.isVisible();
15020     },
15021
15022     // private
15023     animHide : function(callback){
15024         var b = Roo.get(this.animateTarget).getBox();
15025         this.proxy.show();
15026         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15027         this.el.hide();
15028         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15029                     this.hideEl.createDelegate(this, [callback]));
15030     },
15031
15032     /**
15033      * Hides the dialog.
15034      * @param {Function} callback (optional) Function to call when the dialog is hidden
15035      * @return {Roo.BasicDialog} this
15036      */
15037     hide : function(callback){
15038         if (this.fireEvent("beforehide", this) === false){
15039             return;
15040         }
15041         if(this.shadow){
15042             this.shadow.hide();
15043         }
15044         if(this.shim) {
15045           this.shim.hide();
15046         }
15047         // sometimes animateTarget seems to get set.. causing problems...
15048         // this just double checks..
15049         if(this.animateTarget && Roo.get(this.animateTarget)) {
15050            this.animHide(callback);
15051         }else{
15052             this.el.hide();
15053             this.hideEl(callback);
15054         }
15055         return this;
15056     },
15057
15058     // private
15059     hideEl : function(callback){
15060         this.proxy.hide();
15061         if(this.modal){
15062             this.mask.hide();
15063             Roo.get(document.body).removeClass("x-body-masked");
15064         }
15065         this.fireEvent("hide", this);
15066         if(typeof callback == "function"){
15067             callback();
15068         }
15069     },
15070
15071     // private
15072     hideAction : function(){
15073         this.setLeft("-10000px");
15074         this.setTop("-10000px");
15075         this.setStyle("visibility", "hidden");
15076     },
15077
15078     // private
15079     refreshSize : function(){
15080         this.size = this.el.getSize();
15081         this.xy = this.el.getXY();
15082         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15083     },
15084
15085     // private
15086     // z-index is managed by the DialogManager and may be overwritten at any time
15087     setZIndex : function(index){
15088         if(this.modal){
15089             this.mask.setStyle("z-index", index);
15090         }
15091         if(this.shim){
15092             this.shim.setStyle("z-index", ++index);
15093         }
15094         if(this.shadow){
15095             this.shadow.setZIndex(++index);
15096         }
15097         this.el.setStyle("z-index", ++index);
15098         if(this.proxy){
15099             this.proxy.setStyle("z-index", ++index);
15100         }
15101         if(this.resizer){
15102             this.resizer.proxy.setStyle("z-index", ++index);
15103         }
15104
15105         this.lastZIndex = index;
15106     },
15107
15108     /**
15109      * Returns the element for this dialog
15110      * @return {Roo.Element} The underlying dialog Element
15111      */
15112     getEl : function(){
15113         return this.el;
15114     }
15115 });
15116
15117 /**
15118  * @class Roo.DialogManager
15119  * Provides global access to BasicDialogs that have been created and
15120  * support for z-indexing (layering) multiple open dialogs.
15121  */
15122 Roo.DialogManager = function(){
15123     var list = {};
15124     var accessList = [];
15125     var front = null;
15126
15127     // private
15128     var sortDialogs = function(d1, d2){
15129         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15130     };
15131
15132     // private
15133     var orderDialogs = function(){
15134         accessList.sort(sortDialogs);
15135         var seed = Roo.DialogManager.zseed;
15136         for(var i = 0, len = accessList.length; i < len; i++){
15137             var dlg = accessList[i];
15138             if(dlg){
15139                 dlg.setZIndex(seed + (i*10));
15140             }
15141         }
15142     };
15143
15144     return {
15145         /**
15146          * The starting z-index for BasicDialogs (defaults to 9000)
15147          * @type Number The z-index value
15148          */
15149         zseed : 9000,
15150
15151         // private
15152         register : function(dlg){
15153             list[dlg.id] = dlg;
15154             accessList.push(dlg);
15155         },
15156
15157         // private
15158         unregister : function(dlg){
15159             delete list[dlg.id];
15160             var i=0;
15161             var len=0;
15162             if(!accessList.indexOf){
15163                 for(  i = 0, len = accessList.length; i < len; i++){
15164                     if(accessList[i] == dlg){
15165                         accessList.splice(i, 1);
15166                         return;
15167                     }
15168                 }
15169             }else{
15170                  i = accessList.indexOf(dlg);
15171                 if(i != -1){
15172                     accessList.splice(i, 1);
15173                 }
15174             }
15175         },
15176
15177         /**
15178          * Gets a registered dialog by id
15179          * @param {String/Object} id The id of the dialog or a dialog
15180          * @return {Roo.BasicDialog} this
15181          */
15182         get : function(id){
15183             return typeof id == "object" ? id : list[id];
15184         },
15185
15186         /**
15187          * Brings the specified dialog to the front
15188          * @param {String/Object} dlg The id of the dialog or a dialog
15189          * @return {Roo.BasicDialog} this
15190          */
15191         bringToFront : function(dlg){
15192             dlg = this.get(dlg);
15193             if(dlg != front){
15194                 front = dlg;
15195                 dlg._lastAccess = new Date().getTime();
15196                 orderDialogs();
15197             }
15198             return dlg;
15199         },
15200
15201         /**
15202          * Sends the specified dialog to the back
15203          * @param {String/Object} dlg The id of the dialog or a dialog
15204          * @return {Roo.BasicDialog} this
15205          */
15206         sendToBack : function(dlg){
15207             dlg = this.get(dlg);
15208             dlg._lastAccess = -(new Date().getTime());
15209             orderDialogs();
15210             return dlg;
15211         },
15212
15213         /**
15214          * Hides all dialogs
15215          */
15216         hideAll : function(){
15217             for(var id in list){
15218                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15219                     list[id].hide();
15220                 }
15221             }
15222         }
15223     };
15224 }();
15225
15226 /**
15227  * @class Roo.LayoutDialog
15228  * @extends Roo.BasicDialog
15229  * Dialog which provides adjustments for working with a layout in a Dialog.
15230  * Add your necessary layout config options to the dialog's config.<br>
15231  * Example usage (including a nested layout):
15232  * <pre><code>
15233 if(!dialog){
15234     dialog = new Roo.LayoutDialog("download-dlg", {
15235         modal: true,
15236         width:600,
15237         height:450,
15238         shadow:true,
15239         minWidth:500,
15240         minHeight:350,
15241         autoTabs:true,
15242         proxyDrag:true,
15243         // layout config merges with the dialog config
15244         center:{
15245             tabPosition: "top",
15246             alwaysShowTabs: true
15247         }
15248     });
15249     dialog.addKeyListener(27, dialog.hide, dialog);
15250     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15251     dialog.addButton("Build It!", this.getDownload, this);
15252
15253     // we can even add nested layouts
15254     var innerLayout = new Roo.BorderLayout("dl-inner", {
15255         east: {
15256             initialSize: 200,
15257             autoScroll:true,
15258             split:true
15259         },
15260         center: {
15261             autoScroll:true
15262         }
15263     });
15264     innerLayout.beginUpdate();
15265     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15266     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15267     innerLayout.endUpdate(true);
15268
15269     var layout = dialog.getLayout();
15270     layout.beginUpdate();
15271     layout.add("center", new Roo.ContentPanel("standard-panel",
15272                         {title: "Download the Source", fitToFrame:true}));
15273     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15274                {title: "Build your own roo.js"}));
15275     layout.getRegion("center").showPanel(sp);
15276     layout.endUpdate();
15277 }
15278 </code></pre>
15279     * @constructor
15280     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15281     * @param {Object} config configuration options
15282   */
15283 Roo.LayoutDialog = function(el, cfg){
15284     
15285     var config=  cfg;
15286     if (typeof(cfg) == 'undefined') {
15287         config = Roo.apply({}, el);
15288         // not sure why we use documentElement here.. - it should always be body.
15289         // IE7 borks horribly if we use documentElement.
15290         // webkit also does not like documentElement - it creates a body element...
15291         el = Roo.get( document.body || document.documentElement ).createChild();
15292         //config.autoCreate = true;
15293     }
15294     
15295     
15296     config.autoTabs = false;
15297     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15298     this.body.setStyle({overflow:"hidden", position:"relative"});
15299     this.layout = new Roo.BorderLayout(this.body.dom, config);
15300     this.layout.monitorWindowResize = false;
15301     this.el.addClass("x-dlg-auto-layout");
15302     // fix case when center region overwrites center function
15303     this.center = Roo.BasicDialog.prototype.center;
15304     this.on("show", this.layout.layout, this.layout, true);
15305     if (config.items) {
15306         var xitems = config.items;
15307         delete config.items;
15308         Roo.each(xitems, this.addxtype, this);
15309     }
15310     
15311     
15312 };
15313 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15314     /**
15315      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15316      * @deprecated
15317      */
15318     endUpdate : function(){
15319         this.layout.endUpdate();
15320     },
15321
15322     /**
15323      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15324      *  @deprecated
15325      */
15326     beginUpdate : function(){
15327         this.layout.beginUpdate();
15328     },
15329
15330     /**
15331      * Get the BorderLayout for this dialog
15332      * @return {Roo.BorderLayout}
15333      */
15334     getLayout : function(){
15335         return this.layout;
15336     },
15337
15338     showEl : function(){
15339         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15340         if(Roo.isIE7){
15341             this.layout.layout();
15342         }
15343     },
15344
15345     // private
15346     // Use the syncHeightBeforeShow config option to control this automatically
15347     syncBodyHeight : function(){
15348         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15349         if(this.layout){this.layout.layout();}
15350     },
15351     
15352       /**
15353      * Add an xtype element (actually adds to the layout.)
15354      * @return {Object} xdata xtype object data.
15355      */
15356     
15357     addxtype : function(c) {
15358         return this.layout.addxtype(c);
15359     }
15360 });/*
15361  * Based on:
15362  * Ext JS Library 1.1.1
15363  * Copyright(c) 2006-2007, Ext JS, LLC.
15364  *
15365  * Originally Released Under LGPL - original licence link has changed is not relivant.
15366  *
15367  * Fork - LGPL
15368  * <script type="text/javascript">
15369  */
15370  
15371 /**
15372  * @class Roo.MessageBox
15373  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15374  * Example usage:
15375  *<pre><code>
15376 // Basic alert:
15377 Roo.Msg.alert('Status', 'Changes saved successfully.');
15378
15379 // Prompt for user data:
15380 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15381     if (btn == 'ok'){
15382         // process text value...
15383     }
15384 });
15385
15386 // Show a dialog using config options:
15387 Roo.Msg.show({
15388    title:'Save Changes?',
15389    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15390    buttons: Roo.Msg.YESNOCANCEL,
15391    fn: processResult,
15392    animEl: 'elId'
15393 });
15394 </code></pre>
15395  * @singleton
15396  */
15397 Roo.MessageBox = function(){
15398     var dlg, opt, mask, waitTimer;
15399     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15400     var buttons, activeTextEl, bwidth;
15401
15402     // private
15403     var handleButton = function(button){
15404         dlg.hide();
15405         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15406     };
15407
15408     // private
15409     var handleHide = function(){
15410         if(opt && opt.cls){
15411             dlg.el.removeClass(opt.cls);
15412         }
15413         if(waitTimer){
15414             Roo.TaskMgr.stop(waitTimer);
15415             waitTimer = null;
15416         }
15417     };
15418
15419     // private
15420     var updateButtons = function(b){
15421         var width = 0;
15422         if(!b){
15423             buttons["ok"].hide();
15424             buttons["cancel"].hide();
15425             buttons["yes"].hide();
15426             buttons["no"].hide();
15427             dlg.footer.dom.style.display = 'none';
15428             return width;
15429         }
15430         dlg.footer.dom.style.display = '';
15431         for(var k in buttons){
15432             if(typeof buttons[k] != "function"){
15433                 if(b[k]){
15434                     buttons[k].show();
15435                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15436                     width += buttons[k].el.getWidth()+15;
15437                 }else{
15438                     buttons[k].hide();
15439                 }
15440             }
15441         }
15442         return width;
15443     };
15444
15445     // private
15446     var handleEsc = function(d, k, e){
15447         if(opt && opt.closable !== false){
15448             dlg.hide();
15449         }
15450         if(e){
15451             e.stopEvent();
15452         }
15453     };
15454
15455     return {
15456         /**
15457          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15458          * @return {Roo.BasicDialog} The BasicDialog element
15459          */
15460         getDialog : function(){
15461            if(!dlg){
15462                 dlg = new Roo.BasicDialog("x-msg-box", {
15463                     autoCreate : true,
15464                     shadow: true,
15465                     draggable: true,
15466                     resizable:false,
15467                     constraintoviewport:false,
15468                     fixedcenter:true,
15469                     collapsible : false,
15470                     shim:true,
15471                     modal: true,
15472                     width:400, height:100,
15473                     buttonAlign:"center",
15474                     closeClick : function(){
15475                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15476                             handleButton("no");
15477                         }else{
15478                             handleButton("cancel");
15479                         }
15480                     }
15481                 });
15482                 dlg.on("hide", handleHide);
15483                 mask = dlg.mask;
15484                 dlg.addKeyListener(27, handleEsc);
15485                 buttons = {};
15486                 var bt = this.buttonText;
15487                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15488                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15489                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15490                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15491                 bodyEl = dlg.body.createChild({
15492
15493                     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>'
15494                 });
15495                 msgEl = bodyEl.dom.firstChild;
15496                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15497                 textboxEl.enableDisplayMode();
15498                 textboxEl.addKeyListener([10,13], function(){
15499                     if(dlg.isVisible() && opt && opt.buttons){
15500                         if(opt.buttons.ok){
15501                             handleButton("ok");
15502                         }else if(opt.buttons.yes){
15503                             handleButton("yes");
15504                         }
15505                     }
15506                 });
15507                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15508                 textareaEl.enableDisplayMode();
15509                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15510                 progressEl.enableDisplayMode();
15511                 var pf = progressEl.dom.firstChild;
15512                 if (pf) {
15513                     pp = Roo.get(pf.firstChild);
15514                     pp.setHeight(pf.offsetHeight);
15515                 }
15516                 
15517             }
15518             return dlg;
15519         },
15520
15521         /**
15522          * Updates the message box body text
15523          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15524          * the XHTML-compliant non-breaking space character '&amp;#160;')
15525          * @return {Roo.MessageBox} This message box
15526          */
15527         updateText : function(text){
15528             if(!dlg.isVisible() && !opt.width){
15529                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15530             }
15531             msgEl.innerHTML = text || '&#160;';
15532             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15533                         Math.max(opt.minWidth || this.minWidth, bwidth));
15534             if(opt.prompt){
15535                 activeTextEl.setWidth(w);
15536             }
15537             if(dlg.isVisible()){
15538                 dlg.fixedcenter = false;
15539             }
15540             dlg.setContentSize(w, bodyEl.getHeight());
15541             if(dlg.isVisible()){
15542                 dlg.fixedcenter = true;
15543             }
15544             return this;
15545         },
15546
15547         /**
15548          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15549          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15550          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15551          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15552          * @return {Roo.MessageBox} This message box
15553          */
15554         updateProgress : function(value, text){
15555             if(text){
15556                 this.updateText(text);
15557             }
15558             if (pp) { // weird bug on my firefox - for some reason this is not defined
15559                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15560             }
15561             return this;
15562         },        
15563
15564         /**
15565          * Returns true if the message box is currently displayed
15566          * @return {Boolean} True if the message box is visible, else false
15567          */
15568         isVisible : function(){
15569             return dlg && dlg.isVisible();  
15570         },
15571
15572         /**
15573          * Hides the message box if it is displayed
15574          */
15575         hide : function(){
15576             if(this.isVisible()){
15577                 dlg.hide();
15578             }  
15579         },
15580
15581         /**
15582          * Displays a new message box, or reinitializes an existing message box, based on the config options
15583          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15584          * The following config object properties are supported:
15585          * <pre>
15586 Property    Type             Description
15587 ----------  ---------------  ------------------------------------------------------------------------------------
15588 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15589                                    closes (defaults to undefined)
15590 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15591                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15592 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15593                                    progress and wait dialogs will ignore this property and always hide the
15594                                    close button as they can only be closed programmatically.
15595 cls               String           A custom CSS class to apply to the message box element
15596 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15597                                    displayed (defaults to 75)
15598 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15599                                    function will be btn (the name of the button that was clicked, if applicable,
15600                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15601                                    Progress and wait dialogs will ignore this option since they do not respond to
15602                                    user actions and can only be closed programmatically, so any required function
15603                                    should be called by the same code after it closes the dialog.
15604 icon              String           A CSS class that provides a background image to be used as an icon for
15605                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15606 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15607 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15608 modal             Boolean          False to allow user interaction with the page while the message box is
15609                                    displayed (defaults to true)
15610 msg               String           A string that will replace the existing message box body text (defaults
15611                                    to the XHTML-compliant non-breaking space character '&#160;')
15612 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15613 progress          Boolean          True to display a progress bar (defaults to false)
15614 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15615 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15616 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15617 title             String           The title text
15618 value             String           The string value to set into the active textbox element if displayed
15619 wait              Boolean          True to display a progress bar (defaults to false)
15620 width             Number           The width of the dialog in pixels
15621 </pre>
15622          *
15623          * Example usage:
15624          * <pre><code>
15625 Roo.Msg.show({
15626    title: 'Address',
15627    msg: 'Please enter your address:',
15628    width: 300,
15629    buttons: Roo.MessageBox.OKCANCEL,
15630    multiline: true,
15631    fn: saveAddress,
15632    animEl: 'addAddressBtn'
15633 });
15634 </code></pre>
15635          * @param {Object} config Configuration options
15636          * @return {Roo.MessageBox} This message box
15637          */
15638         show : function(options){
15639             if(this.isVisible()){
15640                 this.hide();
15641             }
15642             var d = this.getDialog();
15643             opt = options;
15644             d.setTitle(opt.title || "&#160;");
15645             d.close.setDisplayed(opt.closable !== false);
15646             activeTextEl = textboxEl;
15647             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15648             if(opt.prompt){
15649                 if(opt.multiline){
15650                     textboxEl.hide();
15651                     textareaEl.show();
15652                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15653                         opt.multiline : this.defaultTextHeight);
15654                     activeTextEl = textareaEl;
15655                 }else{
15656                     textboxEl.show();
15657                     textareaEl.hide();
15658                 }
15659             }else{
15660                 textboxEl.hide();
15661                 textareaEl.hide();
15662             }
15663             progressEl.setDisplayed(opt.progress === true);
15664             this.updateProgress(0);
15665             activeTextEl.dom.value = opt.value || "";
15666             if(opt.prompt){
15667                 dlg.setDefaultButton(activeTextEl);
15668             }else{
15669                 var bs = opt.buttons;
15670                 var db = null;
15671                 if(bs && bs.ok){
15672                     db = buttons["ok"];
15673                 }else if(bs && bs.yes){
15674                     db = buttons["yes"];
15675                 }
15676                 dlg.setDefaultButton(db);
15677             }
15678             bwidth = updateButtons(opt.buttons);
15679             this.updateText(opt.msg);
15680             if(opt.cls){
15681                 d.el.addClass(opt.cls);
15682             }
15683             d.proxyDrag = opt.proxyDrag === true;
15684             d.modal = opt.modal !== false;
15685             d.mask = opt.modal !== false ? mask : false;
15686             if(!d.isVisible()){
15687                 // force it to the end of the z-index stack so it gets a cursor in FF
15688                 document.body.appendChild(dlg.el.dom);
15689                 d.animateTarget = null;
15690                 d.show(options.animEl);
15691             }
15692             return this;
15693         },
15694
15695         /**
15696          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15697          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15698          * and closing the message box when the process is complete.
15699          * @param {String} title The title bar text
15700          * @param {String} msg The message box body text
15701          * @return {Roo.MessageBox} This message box
15702          */
15703         progress : function(title, msg){
15704             this.show({
15705                 title : title,
15706                 msg : msg,
15707                 buttons: false,
15708                 progress:true,
15709                 closable:false,
15710                 minWidth: this.minProgressWidth,
15711                 modal : true
15712             });
15713             return this;
15714         },
15715
15716         /**
15717          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15718          * If a callback function is passed it will be called after the user clicks the button, and the
15719          * id of the button that was clicked will be passed as the only parameter to the callback
15720          * (could also be the top-right close button).
15721          * @param {String} title The title bar text
15722          * @param {String} msg The message box body text
15723          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15724          * @param {Object} scope (optional) The scope of the callback function
15725          * @return {Roo.MessageBox} This message box
15726          */
15727         alert : function(title, msg, fn, scope){
15728             this.show({
15729                 title : title,
15730                 msg : msg,
15731                 buttons: this.OK,
15732                 fn: fn,
15733                 scope : scope,
15734                 modal : true
15735             });
15736             return this;
15737         },
15738
15739         /**
15740          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15741          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15742          * You are responsible for closing the message box when the process is complete.
15743          * @param {String} msg The message box body text
15744          * @param {String} title (optional) The title bar text
15745          * @return {Roo.MessageBox} This message box
15746          */
15747         wait : function(msg, title){
15748             this.show({
15749                 title : title,
15750                 msg : msg,
15751                 buttons: false,
15752                 closable:false,
15753                 progress:true,
15754                 modal:true,
15755                 width:300,
15756                 wait:true
15757             });
15758             waitTimer = Roo.TaskMgr.start({
15759                 run: function(i){
15760                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15761                 },
15762                 interval: 1000
15763             });
15764             return this;
15765         },
15766
15767         /**
15768          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15769          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15770          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15771          * @param {String} title The title bar text
15772          * @param {String} msg The message box body text
15773          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15774          * @param {Object} scope (optional) The scope of the callback function
15775          * @return {Roo.MessageBox} This message box
15776          */
15777         confirm : function(title, msg, fn, scope){
15778             this.show({
15779                 title : title,
15780                 msg : msg,
15781                 buttons: this.YESNO,
15782                 fn: fn,
15783                 scope : scope,
15784                 modal : true
15785             });
15786             return this;
15787         },
15788
15789         /**
15790          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15791          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15792          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15793          * (could also be the top-right close button) and the text that was entered will be passed as the two
15794          * parameters to the callback.
15795          * @param {String} title The title bar text
15796          * @param {String} msg The message box body text
15797          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15798          * @param {Object} scope (optional) The scope of the callback function
15799          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15800          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15801          * @return {Roo.MessageBox} This message box
15802          */
15803         prompt : function(title, msg, fn, scope, multiline){
15804             this.show({
15805                 title : title,
15806                 msg : msg,
15807                 buttons: this.OKCANCEL,
15808                 fn: fn,
15809                 minWidth:250,
15810                 scope : scope,
15811                 prompt:true,
15812                 multiline: multiline,
15813                 modal : true
15814             });
15815             return this;
15816         },
15817
15818         /**
15819          * Button config that displays a single OK button
15820          * @type Object
15821          */
15822         OK : {ok:true},
15823         /**
15824          * Button config that displays Yes and No buttons
15825          * @type Object
15826          */
15827         YESNO : {yes:true, no:true},
15828         /**
15829          * Button config that displays OK and Cancel buttons
15830          * @type Object
15831          */
15832         OKCANCEL : {ok:true, cancel:true},
15833         /**
15834          * Button config that displays Yes, No and Cancel buttons
15835          * @type Object
15836          */
15837         YESNOCANCEL : {yes:true, no:true, cancel:true},
15838
15839         /**
15840          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15841          * @type Number
15842          */
15843         defaultTextHeight : 75,
15844         /**
15845          * The maximum width in pixels of the message box (defaults to 600)
15846          * @type Number
15847          */
15848         maxWidth : 600,
15849         /**
15850          * The minimum width in pixels of the message box (defaults to 100)
15851          * @type Number
15852          */
15853         minWidth : 100,
15854         /**
15855          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15856          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15857          * @type Number
15858          */
15859         minProgressWidth : 250,
15860         /**
15861          * An object containing the default button text strings that can be overriden for localized language support.
15862          * Supported properties are: ok, cancel, yes and no.
15863          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15864          * @type Object
15865          */
15866         buttonText : {
15867             ok : "OK",
15868             cancel : "Cancel",
15869             yes : "Yes",
15870             no : "No"
15871         }
15872     };
15873 }();
15874
15875 /**
15876  * Shorthand for {@link Roo.MessageBox}
15877  */
15878 Roo.Msg = Roo.MessageBox;/*
15879  * Based on:
15880  * Ext JS Library 1.1.1
15881  * Copyright(c) 2006-2007, Ext JS, LLC.
15882  *
15883  * Originally Released Under LGPL - original licence link has changed is not relivant.
15884  *
15885  * Fork - LGPL
15886  * <script type="text/javascript">
15887  */
15888 /**
15889  * @class Roo.QuickTips
15890  * Provides attractive and customizable tooltips for any element.
15891  * @singleton
15892  */
15893 Roo.QuickTips = function(){
15894     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15895     var ce, bd, xy, dd;
15896     var visible = false, disabled = true, inited = false;
15897     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15898     
15899     var onOver = function(e){
15900         if(disabled){
15901             return;
15902         }
15903         var t = e.getTarget();
15904         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15905             return;
15906         }
15907         if(ce && t == ce.el){
15908             clearTimeout(hideProc);
15909             return;
15910         }
15911         if(t && tagEls[t.id]){
15912             tagEls[t.id].el = t;
15913             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15914             return;
15915         }
15916         var ttp, et = Roo.fly(t);
15917         var ns = cfg.namespace;
15918         if(tm.interceptTitles && t.title){
15919             ttp = t.title;
15920             t.qtip = ttp;
15921             t.removeAttribute("title");
15922             e.preventDefault();
15923         }else{
15924             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15925         }
15926         if(ttp){
15927             showProc = show.defer(tm.showDelay, tm, [{
15928                 el: t, 
15929                 text: ttp, 
15930                 width: et.getAttributeNS(ns, cfg.width),
15931                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15932                 title: et.getAttributeNS(ns, cfg.title),
15933                     cls: et.getAttributeNS(ns, cfg.cls)
15934             }]);
15935         }
15936     };
15937     
15938     var onOut = function(e){
15939         clearTimeout(showProc);
15940         var t = e.getTarget();
15941         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15942             hideProc = setTimeout(hide, tm.hideDelay);
15943         }
15944     };
15945     
15946     var onMove = function(e){
15947         if(disabled){
15948             return;
15949         }
15950         xy = e.getXY();
15951         xy[1] += 18;
15952         if(tm.trackMouse && ce){
15953             el.setXY(xy);
15954         }
15955     };
15956     
15957     var onDown = function(e){
15958         clearTimeout(showProc);
15959         clearTimeout(hideProc);
15960         if(!e.within(el)){
15961             if(tm.hideOnClick){
15962                 hide();
15963                 tm.disable();
15964                 tm.enable.defer(100, tm);
15965             }
15966         }
15967     };
15968     
15969     var getPad = function(){
15970         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15971     };
15972
15973     var show = function(o){
15974         if(disabled){
15975             return;
15976         }
15977         clearTimeout(dismissProc);
15978         ce = o;
15979         if(removeCls){ // in case manually hidden
15980             el.removeClass(removeCls);
15981             removeCls = null;
15982         }
15983         if(ce.cls){
15984             el.addClass(ce.cls);
15985             removeCls = ce.cls;
15986         }
15987         if(ce.title){
15988             tipTitle.update(ce.title);
15989             tipTitle.show();
15990         }else{
15991             tipTitle.update('');
15992             tipTitle.hide();
15993         }
15994         el.dom.style.width  = tm.maxWidth+'px';
15995         //tipBody.dom.style.width = '';
15996         tipBodyText.update(o.text);
15997         var p = getPad(), w = ce.width;
15998         if(!w){
15999             var td = tipBodyText.dom;
16000             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16001             if(aw > tm.maxWidth){
16002                 w = tm.maxWidth;
16003             }else if(aw < tm.minWidth){
16004                 w = tm.minWidth;
16005             }else{
16006                 w = aw;
16007             }
16008         }
16009         //tipBody.setWidth(w);
16010         el.setWidth(parseInt(w, 10) + p);
16011         if(ce.autoHide === false){
16012             close.setDisplayed(true);
16013             if(dd){
16014                 dd.unlock();
16015             }
16016         }else{
16017             close.setDisplayed(false);
16018             if(dd){
16019                 dd.lock();
16020             }
16021         }
16022         if(xy){
16023             el.avoidY = xy[1]-18;
16024             el.setXY(xy);
16025         }
16026         if(tm.animate){
16027             el.setOpacity(.1);
16028             el.setStyle("visibility", "visible");
16029             el.fadeIn({callback: afterShow});
16030         }else{
16031             afterShow();
16032         }
16033     };
16034     
16035     var afterShow = function(){
16036         if(ce){
16037             el.show();
16038             esc.enable();
16039             if(tm.autoDismiss && ce.autoHide !== false){
16040                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16041             }
16042         }
16043     };
16044     
16045     var hide = function(noanim){
16046         clearTimeout(dismissProc);
16047         clearTimeout(hideProc);
16048         ce = null;
16049         if(el.isVisible()){
16050             esc.disable();
16051             if(noanim !== true && tm.animate){
16052                 el.fadeOut({callback: afterHide});
16053             }else{
16054                 afterHide();
16055             } 
16056         }
16057     };
16058     
16059     var afterHide = function(){
16060         el.hide();
16061         if(removeCls){
16062             el.removeClass(removeCls);
16063             removeCls = null;
16064         }
16065     };
16066     
16067     return {
16068         /**
16069         * @cfg {Number} minWidth
16070         * The minimum width of the quick tip (defaults to 40)
16071         */
16072        minWidth : 40,
16073         /**
16074         * @cfg {Number} maxWidth
16075         * The maximum width of the quick tip (defaults to 300)
16076         */
16077        maxWidth : 300,
16078         /**
16079         * @cfg {Boolean} interceptTitles
16080         * True to automatically use the element's DOM title value if available (defaults to false)
16081         */
16082        interceptTitles : false,
16083         /**
16084         * @cfg {Boolean} trackMouse
16085         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16086         */
16087        trackMouse : false,
16088         /**
16089         * @cfg {Boolean} hideOnClick
16090         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16091         */
16092        hideOnClick : true,
16093         /**
16094         * @cfg {Number} showDelay
16095         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16096         */
16097        showDelay : 500,
16098         /**
16099         * @cfg {Number} hideDelay
16100         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16101         */
16102        hideDelay : 200,
16103         /**
16104         * @cfg {Boolean} autoHide
16105         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16106         * Used in conjunction with hideDelay.
16107         */
16108        autoHide : true,
16109         /**
16110         * @cfg {Boolean}
16111         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16112         * (defaults to true).  Used in conjunction with autoDismissDelay.
16113         */
16114        autoDismiss : true,
16115         /**
16116         * @cfg {Number}
16117         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16118         */
16119        autoDismissDelay : 5000,
16120        /**
16121         * @cfg {Boolean} animate
16122         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16123         */
16124        animate : false,
16125
16126        /**
16127         * @cfg {String} title
16128         * Title text to display (defaults to '').  This can be any valid HTML markup.
16129         */
16130         title: '',
16131        /**
16132         * @cfg {String} text
16133         * Body text to display (defaults to '').  This can be any valid HTML markup.
16134         */
16135         text : '',
16136        /**
16137         * @cfg {String} cls
16138         * A CSS class to apply to the base quick tip element (defaults to '').
16139         */
16140         cls : '',
16141        /**
16142         * @cfg {Number} width
16143         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16144         * minWidth or maxWidth.
16145         */
16146         width : null,
16147
16148     /**
16149      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16150      * or display QuickTips in a page.
16151      */
16152        init : function(){
16153           tm = Roo.QuickTips;
16154           cfg = tm.tagConfig;
16155           if(!inited){
16156               if(!Roo.isReady){ // allow calling of init() before onReady
16157                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16158                   return;
16159               }
16160               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16161               el.fxDefaults = {stopFx: true};
16162               // maximum custom styling
16163               //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>');
16164               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>');              
16165               tipTitle = el.child('h3');
16166               tipTitle.enableDisplayMode("block");
16167               tipBody = el.child('div.x-tip-bd');
16168               tipBodyText = el.child('div.x-tip-bd-inner');
16169               //bdLeft = el.child('div.x-tip-bd-left');
16170               //bdRight = el.child('div.x-tip-bd-right');
16171               close = el.child('div.x-tip-close');
16172               close.enableDisplayMode("block");
16173               close.on("click", hide);
16174               var d = Roo.get(document);
16175               d.on("mousedown", onDown);
16176               d.on("mouseover", onOver);
16177               d.on("mouseout", onOut);
16178               d.on("mousemove", onMove);
16179               esc = d.addKeyListener(27, hide);
16180               esc.disable();
16181               if(Roo.dd.DD){
16182                   dd = el.initDD("default", null, {
16183                       onDrag : function(){
16184                           el.sync();  
16185                       }
16186                   });
16187                   dd.setHandleElId(tipTitle.id);
16188                   dd.lock();
16189               }
16190               inited = true;
16191           }
16192           this.enable(); 
16193        },
16194
16195     /**
16196      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16197      * are supported:
16198      * <pre>
16199 Property    Type                   Description
16200 ----------  ---------------------  ------------------------------------------------------------------------
16201 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16202      * </ul>
16203      * @param {Object} config The config object
16204      */
16205        register : function(config){
16206            var cs = config instanceof Array ? config : arguments;
16207            for(var i = 0, len = cs.length; i < len; i++) {
16208                var c = cs[i];
16209                var target = c.target;
16210                if(target){
16211                    if(target instanceof Array){
16212                        for(var j = 0, jlen = target.length; j < jlen; j++){
16213                            tagEls[target[j]] = c;
16214                        }
16215                    }else{
16216                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16217                    }
16218                }
16219            }
16220        },
16221
16222     /**
16223      * Removes this quick tip from its element and destroys it.
16224      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16225      */
16226        unregister : function(el){
16227            delete tagEls[Roo.id(el)];
16228        },
16229
16230     /**
16231      * Enable this quick tip.
16232      */
16233        enable : function(){
16234            if(inited && disabled){
16235                locks.pop();
16236                if(locks.length < 1){
16237                    disabled = false;
16238                }
16239            }
16240        },
16241
16242     /**
16243      * Disable this quick tip.
16244      */
16245        disable : function(){
16246           disabled = true;
16247           clearTimeout(showProc);
16248           clearTimeout(hideProc);
16249           clearTimeout(dismissProc);
16250           if(ce){
16251               hide(true);
16252           }
16253           locks.push(1);
16254        },
16255
16256     /**
16257      * Returns true if the quick tip is enabled, else false.
16258      */
16259        isEnabled : function(){
16260             return !disabled;
16261        },
16262
16263         // private
16264        tagConfig : {
16265            namespace : "ext",
16266            attribute : "qtip",
16267            width : "width",
16268            target : "target",
16269            title : "qtitle",
16270            hide : "hide",
16271            cls : "qclass"
16272        }
16273    };
16274 }();
16275
16276 // backwards compat
16277 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16278  * Based on:
16279  * Ext JS Library 1.1.1
16280  * Copyright(c) 2006-2007, Ext JS, LLC.
16281  *
16282  * Originally Released Under LGPL - original licence link has changed is not relivant.
16283  *
16284  * Fork - LGPL
16285  * <script type="text/javascript">
16286  */
16287  
16288
16289 /**
16290  * @class Roo.tree.TreePanel
16291  * @extends Roo.data.Tree
16292
16293  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16294  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16295  * @cfg {Boolean} enableDD true to enable drag and drop
16296  * @cfg {Boolean} enableDrag true to enable just drag
16297  * @cfg {Boolean} enableDrop true to enable just drop
16298  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16299  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16300  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16301  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16302  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16303  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16304  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16305  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16306  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16307  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16308  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16309  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16310  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16311  * @cfg {Function} renderer 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>
16312  * @cfg {Function} rendererTip 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>
16313  * 
16314  * @constructor
16315  * @param {String/HTMLElement/Element} el The container element
16316  * @param {Object} config
16317  */
16318 Roo.tree.TreePanel = function(el, config){
16319     var root = false;
16320     var loader = false;
16321     if (config.root) {
16322         root = config.root;
16323         delete config.root;
16324     }
16325     if (config.loader) {
16326         loader = config.loader;
16327         delete config.loader;
16328     }
16329     
16330     Roo.apply(this, config);
16331     Roo.tree.TreePanel.superclass.constructor.call(this);
16332     this.el = Roo.get(el);
16333     this.el.addClass('x-tree');
16334     //console.log(root);
16335     if (root) {
16336         this.setRootNode( Roo.factory(root, Roo.tree));
16337     }
16338     if (loader) {
16339         this.loader = Roo.factory(loader, Roo.tree);
16340     }
16341    /**
16342     * Read-only. The id of the container element becomes this TreePanel's id.
16343     */
16344    this.id = this.el.id;
16345    this.addEvents({
16346         /**
16347         * @event beforeload
16348         * Fires before a node is loaded, return false to cancel
16349         * @param {Node} node The node being loaded
16350         */
16351         "beforeload" : true,
16352         /**
16353         * @event load
16354         * Fires when a node is loaded
16355         * @param {Node} node The node that was loaded
16356         */
16357         "load" : true,
16358         /**
16359         * @event textchange
16360         * Fires when the text for a node is changed
16361         * @param {Node} node The node
16362         * @param {String} text The new text
16363         * @param {String} oldText The old text
16364         */
16365         "textchange" : true,
16366         /**
16367         * @event beforeexpand
16368         * Fires before a node is expanded, return false to cancel.
16369         * @param {Node} node The node
16370         * @param {Boolean} deep
16371         * @param {Boolean} anim
16372         */
16373         "beforeexpand" : true,
16374         /**
16375         * @event beforecollapse
16376         * Fires before a node is collapsed, return false to cancel.
16377         * @param {Node} node The node
16378         * @param {Boolean} deep
16379         * @param {Boolean} anim
16380         */
16381         "beforecollapse" : true,
16382         /**
16383         * @event expand
16384         * Fires when a node is expanded
16385         * @param {Node} node The node
16386         */
16387         "expand" : true,
16388         /**
16389         * @event disabledchange
16390         * Fires when the disabled status of a node changes
16391         * @param {Node} node The node
16392         * @param {Boolean} disabled
16393         */
16394         "disabledchange" : true,
16395         /**
16396         * @event collapse
16397         * Fires when a node is collapsed
16398         * @param {Node} node The node
16399         */
16400         "collapse" : true,
16401         /**
16402         * @event beforeclick
16403         * Fires before click processing on a node. Return false to cancel the default action.
16404         * @param {Node} node The node
16405         * @param {Roo.EventObject} e The event object
16406         */
16407         "beforeclick":true,
16408         /**
16409         * @event checkchange
16410         * Fires when a node with a checkbox's checked property changes
16411         * @param {Node} this This node
16412         * @param {Boolean} checked
16413         */
16414         "checkchange":true,
16415         /**
16416         * @event click
16417         * Fires when a node is clicked
16418         * @param {Node} node The node
16419         * @param {Roo.EventObject} e The event object
16420         */
16421         "click":true,
16422         /**
16423         * @event dblclick
16424         * Fires when a node is double clicked
16425         * @param {Node} node The node
16426         * @param {Roo.EventObject} e The event object
16427         */
16428         "dblclick":true,
16429         /**
16430         * @event contextmenu
16431         * Fires when a node is right clicked
16432         * @param {Node} node The node
16433         * @param {Roo.EventObject} e The event object
16434         */
16435         "contextmenu":true,
16436         /**
16437         * @event beforechildrenrendered
16438         * Fires right before the child nodes for a node are rendered
16439         * @param {Node} node The node
16440         */
16441         "beforechildrenrendered":true,
16442        /**
16443              * @event startdrag
16444              * Fires when a node starts being dragged
16445              * @param {Roo.tree.TreePanel} this
16446              * @param {Roo.tree.TreeNode} node
16447              * @param {event} e The raw browser event
16448              */ 
16449             "startdrag" : true,
16450             /**
16451              * @event enddrag
16452              * Fires when a drag operation is complete
16453              * @param {Roo.tree.TreePanel} this
16454              * @param {Roo.tree.TreeNode} node
16455              * @param {event} e The raw browser event
16456              */
16457             "enddrag" : true,
16458             /**
16459              * @event dragdrop
16460              * Fires when a dragged node is dropped on a valid DD target
16461              * @param {Roo.tree.TreePanel} this
16462              * @param {Roo.tree.TreeNode} node
16463              * @param {DD} dd The dd it was dropped on
16464              * @param {event} e The raw browser event
16465              */
16466             "dragdrop" : true,
16467             /**
16468              * @event beforenodedrop
16469              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16470              * passed to handlers has the following properties:<br />
16471              * <ul style="padding:5px;padding-left:16px;">
16472              * <li>tree - The TreePanel</li>
16473              * <li>target - The node being targeted for the drop</li>
16474              * <li>data - The drag data from the drag source</li>
16475              * <li>point - The point of the drop - append, above or below</li>
16476              * <li>source - The drag source</li>
16477              * <li>rawEvent - Raw mouse event</li>
16478              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16479              * to be inserted by setting them on this object.</li>
16480              * <li>cancel - Set this to true to cancel the drop.</li>
16481              * </ul>
16482              * @param {Object} dropEvent
16483              */
16484             "beforenodedrop" : true,
16485             /**
16486              * @event nodedrop
16487              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16488              * passed to handlers has the following properties:<br />
16489              * <ul style="padding:5px;padding-left:16px;">
16490              * <li>tree - The TreePanel</li>
16491              * <li>target - The node being targeted for the drop</li>
16492              * <li>data - The drag data from the drag source</li>
16493              * <li>point - The point of the drop - append, above or below</li>
16494              * <li>source - The drag source</li>
16495              * <li>rawEvent - Raw mouse event</li>
16496              * <li>dropNode - Dropped node(s).</li>
16497              * </ul>
16498              * @param {Object} dropEvent
16499              */
16500             "nodedrop" : true,
16501              /**
16502              * @event nodedragover
16503              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16504              * passed to handlers has the following properties:<br />
16505              * <ul style="padding:5px;padding-left:16px;">
16506              * <li>tree - The TreePanel</li>
16507              * <li>target - The node being targeted for the drop</li>
16508              * <li>data - The drag data from the drag source</li>
16509              * <li>point - The point of the drop - append, above or below</li>
16510              * <li>source - The drag source</li>
16511              * <li>rawEvent - Raw mouse event</li>
16512              * <li>dropNode - Drop node(s) provided by the source.</li>
16513              * <li>cancel - Set this to true to signal drop not allowed.</li>
16514              * </ul>
16515              * @param {Object} dragOverEvent
16516              */
16517             "nodedragover" : true
16518         
16519    });
16520    if(this.singleExpand){
16521        this.on("beforeexpand", this.restrictExpand, this);
16522    }
16523 };
16524 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16525     rootVisible : true,
16526     animate: Roo.enableFx,
16527     lines : true,
16528     enableDD : false,
16529     hlDrop : Roo.enableFx,
16530   
16531     renderer: false,
16532     
16533     rendererTip: false,
16534     // private
16535     restrictExpand : function(node){
16536         var p = node.parentNode;
16537         if(p){
16538             if(p.expandedChild && p.expandedChild.parentNode == p){
16539                 p.expandedChild.collapse();
16540             }
16541             p.expandedChild = node;
16542         }
16543     },
16544
16545     // private override
16546     setRootNode : function(node){
16547         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16548         if(!this.rootVisible){
16549             node.ui = new Roo.tree.RootTreeNodeUI(node);
16550         }
16551         return node;
16552     },
16553
16554     /**
16555      * Returns the container element for this TreePanel
16556      */
16557     getEl : function(){
16558         return this.el;
16559     },
16560
16561     /**
16562      * Returns the default TreeLoader for this TreePanel
16563      */
16564     getLoader : function(){
16565         return this.loader;
16566     },
16567
16568     /**
16569      * Expand all nodes
16570      */
16571     expandAll : function(){
16572         this.root.expand(true);
16573     },
16574
16575     /**
16576      * Collapse all nodes
16577      */
16578     collapseAll : function(){
16579         this.root.collapse(true);
16580     },
16581
16582     /**
16583      * Returns the selection model used by this TreePanel
16584      */
16585     getSelectionModel : function(){
16586         if(!this.selModel){
16587             this.selModel = new Roo.tree.DefaultSelectionModel();
16588         }
16589         return this.selModel;
16590     },
16591
16592     /**
16593      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16594      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16595      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16596      * @return {Array}
16597      */
16598     getChecked : function(a, startNode){
16599         startNode = startNode || this.root;
16600         var r = [];
16601         var f = function(){
16602             if(this.attributes.checked){
16603                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16604             }
16605         }
16606         startNode.cascade(f);
16607         return r;
16608     },
16609
16610     /**
16611      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16612      * @param {String} path
16613      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16614      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16615      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16616      */
16617     expandPath : function(path, attr, callback){
16618         attr = attr || "id";
16619         var keys = path.split(this.pathSeparator);
16620         var curNode = this.root;
16621         if(curNode.attributes[attr] != keys[1]){ // invalid root
16622             if(callback){
16623                 callback(false, null);
16624             }
16625             return;
16626         }
16627         var index = 1;
16628         var f = function(){
16629             if(++index == keys.length){
16630                 if(callback){
16631                     callback(true, curNode);
16632                 }
16633                 return;
16634             }
16635             var c = curNode.findChild(attr, keys[index]);
16636             if(!c){
16637                 if(callback){
16638                     callback(false, curNode);
16639                 }
16640                 return;
16641             }
16642             curNode = c;
16643             c.expand(false, false, f);
16644         };
16645         curNode.expand(false, false, f);
16646     },
16647
16648     /**
16649      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16650      * @param {String} path
16651      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16652      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16653      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16654      */
16655     selectPath : function(path, attr, callback){
16656         attr = attr || "id";
16657         var keys = path.split(this.pathSeparator);
16658         var v = keys.pop();
16659         if(keys.length > 0){
16660             var f = function(success, node){
16661                 if(success && node){
16662                     var n = node.findChild(attr, v);
16663                     if(n){
16664                         n.select();
16665                         if(callback){
16666                             callback(true, n);
16667                         }
16668                     }else if(callback){
16669                         callback(false, n);
16670                     }
16671                 }else{
16672                     if(callback){
16673                         callback(false, n);
16674                     }
16675                 }
16676             };
16677             this.expandPath(keys.join(this.pathSeparator), attr, f);
16678         }else{
16679             this.root.select();
16680             if(callback){
16681                 callback(true, this.root);
16682             }
16683         }
16684     },
16685
16686     getTreeEl : function(){
16687         return this.el;
16688     },
16689
16690     /**
16691      * Trigger rendering of this TreePanel
16692      */
16693     render : function(){
16694         if (this.innerCt) {
16695             return this; // stop it rendering more than once!!
16696         }
16697         
16698         this.innerCt = this.el.createChild({tag:"ul",
16699                cls:"x-tree-root-ct " +
16700                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16701
16702         if(this.containerScroll){
16703             Roo.dd.ScrollManager.register(this.el);
16704         }
16705         if((this.enableDD || this.enableDrop) && !this.dropZone){
16706            /**
16707             * The dropZone used by this tree if drop is enabled
16708             * @type Roo.tree.TreeDropZone
16709             */
16710              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16711                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16712            });
16713         }
16714         if((this.enableDD || this.enableDrag) && !this.dragZone){
16715            /**
16716             * The dragZone used by this tree if drag is enabled
16717             * @type Roo.tree.TreeDragZone
16718             */
16719             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16720                ddGroup: this.ddGroup || "TreeDD",
16721                scroll: this.ddScroll
16722            });
16723         }
16724         this.getSelectionModel().init(this);
16725         if (!this.root) {
16726             console.log("ROOT not set in tree");
16727             return;
16728         }
16729         this.root.render();
16730         if(!this.rootVisible){
16731             this.root.renderChildren();
16732         }
16733         return this;
16734     }
16735 });/*
16736  * Based on:
16737  * Ext JS Library 1.1.1
16738  * Copyright(c) 2006-2007, Ext JS, LLC.
16739  *
16740  * Originally Released Under LGPL - original licence link has changed is not relivant.
16741  *
16742  * Fork - LGPL
16743  * <script type="text/javascript">
16744  */
16745  
16746
16747 /**
16748  * @class Roo.tree.DefaultSelectionModel
16749  * @extends Roo.util.Observable
16750  * The default single selection for a TreePanel.
16751  */
16752 Roo.tree.DefaultSelectionModel = function(){
16753    this.selNode = null;
16754    
16755    this.addEvents({
16756        /**
16757         * @event selectionchange
16758         * Fires when the selected node changes
16759         * @param {DefaultSelectionModel} this
16760         * @param {TreeNode} node the new selection
16761         */
16762        "selectionchange" : true,
16763
16764        /**
16765         * @event beforeselect
16766         * Fires before the selected node changes, return false to cancel the change
16767         * @param {DefaultSelectionModel} this
16768         * @param {TreeNode} node the new selection
16769         * @param {TreeNode} node the old selection
16770         */
16771        "beforeselect" : true
16772    });
16773 };
16774
16775 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16776     init : function(tree){
16777         this.tree = tree;
16778         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16779         tree.on("click", this.onNodeClick, this);
16780     },
16781     
16782     onNodeClick : function(node, e){
16783         if (e.ctrlKey && this.selNode == node)  {
16784             this.unselect(node);
16785             return;
16786         }
16787         this.select(node);
16788     },
16789     
16790     /**
16791      * Select a node.
16792      * @param {TreeNode} node The node to select
16793      * @return {TreeNode} The selected node
16794      */
16795     select : function(node){
16796         var last = this.selNode;
16797         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16798             if(last){
16799                 last.ui.onSelectedChange(false);
16800             }
16801             this.selNode = node;
16802             node.ui.onSelectedChange(true);
16803             this.fireEvent("selectionchange", this, node, last);
16804         }
16805         return node;
16806     },
16807     
16808     /**
16809      * Deselect a node.
16810      * @param {TreeNode} node The node to unselect
16811      */
16812     unselect : function(node){
16813         if(this.selNode == node){
16814             this.clearSelections();
16815         }    
16816     },
16817     
16818     /**
16819      * Clear all selections
16820      */
16821     clearSelections : function(){
16822         var n = this.selNode;
16823         if(n){
16824             n.ui.onSelectedChange(false);
16825             this.selNode = null;
16826             this.fireEvent("selectionchange", this, null);
16827         }
16828         return n;
16829     },
16830     
16831     /**
16832      * Get the selected node
16833      * @return {TreeNode} The selected node
16834      */
16835     getSelectedNode : function(){
16836         return this.selNode;    
16837     },
16838     
16839     /**
16840      * Returns true if the node is selected
16841      * @param {TreeNode} node The node to check
16842      * @return {Boolean}
16843      */
16844     isSelected : function(node){
16845         return this.selNode == node;  
16846     },
16847
16848     /**
16849      * Selects the node above the selected node in the tree, intelligently walking the nodes
16850      * @return TreeNode The new selection
16851      */
16852     selectPrevious : function(){
16853         var s = this.selNode || this.lastSelNode;
16854         if(!s){
16855             return null;
16856         }
16857         var ps = s.previousSibling;
16858         if(ps){
16859             if(!ps.isExpanded() || ps.childNodes.length < 1){
16860                 return this.select(ps);
16861             } else{
16862                 var lc = ps.lastChild;
16863                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16864                     lc = lc.lastChild;
16865                 }
16866                 return this.select(lc);
16867             }
16868         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16869             return this.select(s.parentNode);
16870         }
16871         return null;
16872     },
16873
16874     /**
16875      * Selects the node above the selected node in the tree, intelligently walking the nodes
16876      * @return TreeNode The new selection
16877      */
16878     selectNext : function(){
16879         var s = this.selNode || this.lastSelNode;
16880         if(!s){
16881             return null;
16882         }
16883         if(s.firstChild && s.isExpanded()){
16884              return this.select(s.firstChild);
16885          }else if(s.nextSibling){
16886              return this.select(s.nextSibling);
16887          }else if(s.parentNode){
16888             var newS = null;
16889             s.parentNode.bubble(function(){
16890                 if(this.nextSibling){
16891                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16892                     return false;
16893                 }
16894             });
16895             return newS;
16896          }
16897         return null;
16898     },
16899
16900     onKeyDown : function(e){
16901         var s = this.selNode || this.lastSelNode;
16902         // undesirable, but required
16903         var sm = this;
16904         if(!s){
16905             return;
16906         }
16907         var k = e.getKey();
16908         switch(k){
16909              case e.DOWN:
16910                  e.stopEvent();
16911                  this.selectNext();
16912              break;
16913              case e.UP:
16914                  e.stopEvent();
16915                  this.selectPrevious();
16916              break;
16917              case e.RIGHT:
16918                  e.preventDefault();
16919                  if(s.hasChildNodes()){
16920                      if(!s.isExpanded()){
16921                          s.expand();
16922                      }else if(s.firstChild){
16923                          this.select(s.firstChild, e);
16924                      }
16925                  }
16926              break;
16927              case e.LEFT:
16928                  e.preventDefault();
16929                  if(s.hasChildNodes() && s.isExpanded()){
16930                      s.collapse();
16931                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16932                      this.select(s.parentNode, e);
16933                  }
16934              break;
16935         };
16936     }
16937 });
16938
16939 /**
16940  * @class Roo.tree.MultiSelectionModel
16941  * @extends Roo.util.Observable
16942  * Multi selection for a TreePanel.
16943  */
16944 Roo.tree.MultiSelectionModel = function(){
16945    this.selNodes = [];
16946    this.selMap = {};
16947    this.addEvents({
16948        /**
16949         * @event selectionchange
16950         * Fires when the selected nodes change
16951         * @param {MultiSelectionModel} this
16952         * @param {Array} nodes Array of the selected nodes
16953         */
16954        "selectionchange" : true
16955    });
16956 };
16957
16958 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16959     init : function(tree){
16960         this.tree = tree;
16961         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16962         tree.on("click", this.onNodeClick, this);
16963     },
16964     
16965     onNodeClick : function(node, e){
16966         this.select(node, e, e.ctrlKey);
16967     },
16968     
16969     /**
16970      * Select a node.
16971      * @param {TreeNode} node The node to select
16972      * @param {EventObject} e (optional) An event associated with the selection
16973      * @param {Boolean} keepExisting True to retain existing selections
16974      * @return {TreeNode} The selected node
16975      */
16976     select : function(node, e, keepExisting){
16977         if(keepExisting !== true){
16978             this.clearSelections(true);
16979         }
16980         if(this.isSelected(node)){
16981             this.lastSelNode = node;
16982             return node;
16983         }
16984         this.selNodes.push(node);
16985         this.selMap[node.id] = node;
16986         this.lastSelNode = node;
16987         node.ui.onSelectedChange(true);
16988         this.fireEvent("selectionchange", this, this.selNodes);
16989         return node;
16990     },
16991     
16992     /**
16993      * Deselect a node.
16994      * @param {TreeNode} node The node to unselect
16995      */
16996     unselect : function(node){
16997         if(this.selMap[node.id]){
16998             node.ui.onSelectedChange(false);
16999             var sn = this.selNodes;
17000             var index = -1;
17001             if(sn.indexOf){
17002                 index = sn.indexOf(node);
17003             }else{
17004                 for(var i = 0, len = sn.length; i < len; i++){
17005                     if(sn[i] == node){
17006                         index = i;
17007                         break;
17008                     }
17009                 }
17010             }
17011             if(index != -1){
17012                 this.selNodes.splice(index, 1);
17013             }
17014             delete this.selMap[node.id];
17015             this.fireEvent("selectionchange", this, this.selNodes);
17016         }
17017     },
17018     
17019     /**
17020      * Clear all selections
17021      */
17022     clearSelections : function(suppressEvent){
17023         var sn = this.selNodes;
17024         if(sn.length > 0){
17025             for(var i = 0, len = sn.length; i < len; i++){
17026                 sn[i].ui.onSelectedChange(false);
17027             }
17028             this.selNodes = [];
17029             this.selMap = {};
17030             if(suppressEvent !== true){
17031                 this.fireEvent("selectionchange", this, this.selNodes);
17032             }
17033         }
17034     },
17035     
17036     /**
17037      * Returns true if the node is selected
17038      * @param {TreeNode} node The node to check
17039      * @return {Boolean}
17040      */
17041     isSelected : function(node){
17042         return this.selMap[node.id] ? true : false;  
17043     },
17044     
17045     /**
17046      * Returns an array of the selected nodes
17047      * @return {Array}
17048      */
17049     getSelectedNodes : function(){
17050         return this.selNodes;    
17051     },
17052
17053     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17054
17055     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17056
17057     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17058 });/*
17059  * Based on:
17060  * Ext JS Library 1.1.1
17061  * Copyright(c) 2006-2007, Ext JS, LLC.
17062  *
17063  * Originally Released Under LGPL - original licence link has changed is not relivant.
17064  *
17065  * Fork - LGPL
17066  * <script type="text/javascript">
17067  */
17068  
17069 /**
17070  * @class Roo.tree.TreeNode
17071  * @extends Roo.data.Node
17072  * @cfg {String} text The text for this node
17073  * @cfg {Boolean} expanded true to start the node expanded
17074  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17075  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17076  * @cfg {Boolean} disabled true to start the node disabled
17077  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17078  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17079  * @cfg {String} cls A css class to be added to the node
17080  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17081  * @cfg {String} href URL of the link used for the node (defaults to #)
17082  * @cfg {String} hrefTarget target frame for the link
17083  * @cfg {String} qtip An Ext QuickTip for the node
17084  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17085  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17086  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17087  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17088  * (defaults to undefined with no checkbox rendered)
17089  * @constructor
17090  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17091  */
17092 Roo.tree.TreeNode = function(attributes){
17093     attributes = attributes || {};
17094     if(typeof attributes == "string"){
17095         attributes = {text: attributes};
17096     }
17097     this.childrenRendered = false;
17098     this.rendered = false;
17099     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17100     this.expanded = attributes.expanded === true;
17101     this.isTarget = attributes.isTarget !== false;
17102     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17103     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17104
17105     /**
17106      * Read-only. The text for this node. To change it use setText().
17107      * @type String
17108      */
17109     this.text = attributes.text;
17110     /**
17111      * True if this node is disabled.
17112      * @type Boolean
17113      */
17114     this.disabled = attributes.disabled === true;
17115
17116     this.addEvents({
17117         /**
17118         * @event textchange
17119         * Fires when the text for this node is changed
17120         * @param {Node} this This node
17121         * @param {String} text The new text
17122         * @param {String} oldText The old text
17123         */
17124         "textchange" : true,
17125         /**
17126         * @event beforeexpand
17127         * Fires before this node is expanded, return false to cancel.
17128         * @param {Node} this This node
17129         * @param {Boolean} deep
17130         * @param {Boolean} anim
17131         */
17132         "beforeexpand" : true,
17133         /**
17134         * @event beforecollapse
17135         * Fires before this node is collapsed, return false to cancel.
17136         * @param {Node} this This node
17137         * @param {Boolean} deep
17138         * @param {Boolean} anim
17139         */
17140         "beforecollapse" : true,
17141         /**
17142         * @event expand
17143         * Fires when this node is expanded
17144         * @param {Node} this This node
17145         */
17146         "expand" : true,
17147         /**
17148         * @event disabledchange
17149         * Fires when the disabled status of this node changes
17150         * @param {Node} this This node
17151         * @param {Boolean} disabled
17152         */
17153         "disabledchange" : true,
17154         /**
17155         * @event collapse
17156         * Fires when this node is collapsed
17157         * @param {Node} this This node
17158         */
17159         "collapse" : true,
17160         /**
17161         * @event beforeclick
17162         * Fires before click processing. Return false to cancel the default action.
17163         * @param {Node} this This node
17164         * @param {Roo.EventObject} e The event object
17165         */
17166         "beforeclick":true,
17167         /**
17168         * @event checkchange
17169         * Fires when a node with a checkbox's checked property changes
17170         * @param {Node} this This node
17171         * @param {Boolean} checked
17172         */
17173         "checkchange":true,
17174         /**
17175         * @event click
17176         * Fires when this node is clicked
17177         * @param {Node} this This node
17178         * @param {Roo.EventObject} e The event object
17179         */
17180         "click":true,
17181         /**
17182         * @event dblclick
17183         * Fires when this node is double clicked
17184         * @param {Node} this This node
17185         * @param {Roo.EventObject} e The event object
17186         */
17187         "dblclick":true,
17188         /**
17189         * @event contextmenu
17190         * Fires when this node is right clicked
17191         * @param {Node} this This node
17192         * @param {Roo.EventObject} e The event object
17193         */
17194         "contextmenu":true,
17195         /**
17196         * @event beforechildrenrendered
17197         * Fires right before the child nodes for this node are rendered
17198         * @param {Node} this This node
17199         */
17200         "beforechildrenrendered":true
17201     });
17202
17203     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17204
17205     /**
17206      * Read-only. The UI for this node
17207      * @type TreeNodeUI
17208      */
17209     this.ui = new uiClass(this);
17210 };
17211 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17212     preventHScroll: true,
17213     /**
17214      * Returns true if this node is expanded
17215      * @return {Boolean}
17216      */
17217     isExpanded : function(){
17218         return this.expanded;
17219     },
17220
17221     /**
17222      * Returns the UI object for this node
17223      * @return {TreeNodeUI}
17224      */
17225     getUI : function(){
17226         return this.ui;
17227     },
17228
17229     // private override
17230     setFirstChild : function(node){
17231         var of = this.firstChild;
17232         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17233         if(this.childrenRendered && of && node != of){
17234             of.renderIndent(true, true);
17235         }
17236         if(this.rendered){
17237             this.renderIndent(true, true);
17238         }
17239     },
17240
17241     // private override
17242     setLastChild : function(node){
17243         var ol = this.lastChild;
17244         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17245         if(this.childrenRendered && ol && node != ol){
17246             ol.renderIndent(true, true);
17247         }
17248         if(this.rendered){
17249             this.renderIndent(true, true);
17250         }
17251     },
17252
17253     // these methods are overridden to provide lazy rendering support
17254     // private override
17255     appendChild : function(){
17256         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17257         if(node && this.childrenRendered){
17258             node.render();
17259         }
17260         this.ui.updateExpandIcon();
17261         return node;
17262     },
17263
17264     // private override
17265     removeChild : function(node){
17266         this.ownerTree.getSelectionModel().unselect(node);
17267         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17268         // if it's been rendered remove dom node
17269         if(this.childrenRendered){
17270             node.ui.remove();
17271         }
17272         if(this.childNodes.length < 1){
17273             this.collapse(false, false);
17274         }else{
17275             this.ui.updateExpandIcon();
17276         }
17277         if(!this.firstChild) {
17278             this.childrenRendered = false;
17279         }
17280         return node;
17281     },
17282
17283     // private override
17284     insertBefore : function(node, refNode){
17285         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17286         if(newNode && refNode && this.childrenRendered){
17287             node.render();
17288         }
17289         this.ui.updateExpandIcon();
17290         return newNode;
17291     },
17292
17293     /**
17294      * Sets the text for this node
17295      * @param {String} text
17296      */
17297     setText : function(text){
17298         var oldText = this.text;
17299         this.text = text;
17300         this.attributes.text = text;
17301         if(this.rendered){ // event without subscribing
17302             this.ui.onTextChange(this, text, oldText);
17303         }
17304         this.fireEvent("textchange", this, text, oldText);
17305     },
17306
17307     /**
17308      * Triggers selection of this node
17309      */
17310     select : function(){
17311         this.getOwnerTree().getSelectionModel().select(this);
17312     },
17313
17314     /**
17315      * Triggers deselection of this node
17316      */
17317     unselect : function(){
17318         this.getOwnerTree().getSelectionModel().unselect(this);
17319     },
17320
17321     /**
17322      * Returns true if this node is selected
17323      * @return {Boolean}
17324      */
17325     isSelected : function(){
17326         return this.getOwnerTree().getSelectionModel().isSelected(this);
17327     },
17328
17329     /**
17330      * Expand this node.
17331      * @param {Boolean} deep (optional) True to expand all children as well
17332      * @param {Boolean} anim (optional) false to cancel the default animation
17333      * @param {Function} callback (optional) A callback to be called when
17334      * expanding this node completes (does not wait for deep expand to complete).
17335      * Called with 1 parameter, this node.
17336      */
17337     expand : function(deep, anim, callback){
17338         if(!this.expanded){
17339             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17340                 return;
17341             }
17342             if(!this.childrenRendered){
17343                 this.renderChildren();
17344             }
17345             this.expanded = true;
17346             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17347                 this.ui.animExpand(function(){
17348                     this.fireEvent("expand", this);
17349                     if(typeof callback == "function"){
17350                         callback(this);
17351                     }
17352                     if(deep === true){
17353                         this.expandChildNodes(true);
17354                     }
17355                 }.createDelegate(this));
17356                 return;
17357             }else{
17358                 this.ui.expand();
17359                 this.fireEvent("expand", this);
17360                 if(typeof callback == "function"){
17361                     callback(this);
17362                 }
17363             }
17364         }else{
17365            if(typeof callback == "function"){
17366                callback(this);
17367            }
17368         }
17369         if(deep === true){
17370             this.expandChildNodes(true);
17371         }
17372     },
17373
17374     isHiddenRoot : function(){
17375         return this.isRoot && !this.getOwnerTree().rootVisible;
17376     },
17377
17378     /**
17379      * Collapse this node.
17380      * @param {Boolean} deep (optional) True to collapse all children as well
17381      * @param {Boolean} anim (optional) false to cancel the default animation
17382      */
17383     collapse : function(deep, anim){
17384         if(this.expanded && !this.isHiddenRoot()){
17385             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17386                 return;
17387             }
17388             this.expanded = false;
17389             if((this.getOwnerTree().animate && anim !== false) || anim){
17390                 this.ui.animCollapse(function(){
17391                     this.fireEvent("collapse", this);
17392                     if(deep === true){
17393                         this.collapseChildNodes(true);
17394                     }
17395                 }.createDelegate(this));
17396                 return;
17397             }else{
17398                 this.ui.collapse();
17399                 this.fireEvent("collapse", this);
17400             }
17401         }
17402         if(deep === true){
17403             var cs = this.childNodes;
17404             for(var i = 0, len = cs.length; i < len; i++) {
17405                 cs[i].collapse(true, false);
17406             }
17407         }
17408     },
17409
17410     // private
17411     delayedExpand : function(delay){
17412         if(!this.expandProcId){
17413             this.expandProcId = this.expand.defer(delay, this);
17414         }
17415     },
17416
17417     // private
17418     cancelExpand : function(){
17419         if(this.expandProcId){
17420             clearTimeout(this.expandProcId);
17421         }
17422         this.expandProcId = false;
17423     },
17424
17425     /**
17426      * Toggles expanded/collapsed state of the node
17427      */
17428     toggle : function(){
17429         if(this.expanded){
17430             this.collapse();
17431         }else{
17432             this.expand();
17433         }
17434     },
17435
17436     /**
17437      * Ensures all parent nodes are expanded
17438      */
17439     ensureVisible : function(callback){
17440         var tree = this.getOwnerTree();
17441         tree.expandPath(this.parentNode.getPath(), false, function(){
17442             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17443             Roo.callback(callback);
17444         }.createDelegate(this));
17445     },
17446
17447     /**
17448      * Expand all child nodes
17449      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17450      */
17451     expandChildNodes : function(deep){
17452         var cs = this.childNodes;
17453         for(var i = 0, len = cs.length; i < len; i++) {
17454                 cs[i].expand(deep);
17455         }
17456     },
17457
17458     /**
17459      * Collapse all child nodes
17460      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17461      */
17462     collapseChildNodes : function(deep){
17463         var cs = this.childNodes;
17464         for(var i = 0, len = cs.length; i < len; i++) {
17465                 cs[i].collapse(deep);
17466         }
17467     },
17468
17469     /**
17470      * Disables this node
17471      */
17472     disable : function(){
17473         this.disabled = true;
17474         this.unselect();
17475         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17476             this.ui.onDisableChange(this, true);
17477         }
17478         this.fireEvent("disabledchange", this, true);
17479     },
17480
17481     /**
17482      * Enables this node
17483      */
17484     enable : function(){
17485         this.disabled = false;
17486         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17487             this.ui.onDisableChange(this, false);
17488         }
17489         this.fireEvent("disabledchange", this, false);
17490     },
17491
17492     // private
17493     renderChildren : function(suppressEvent){
17494         if(suppressEvent !== false){
17495             this.fireEvent("beforechildrenrendered", this);
17496         }
17497         var cs = this.childNodes;
17498         for(var i = 0, len = cs.length; i < len; i++){
17499             cs[i].render(true);
17500         }
17501         this.childrenRendered = true;
17502     },
17503
17504     // private
17505     sort : function(fn, scope){
17506         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17507         if(this.childrenRendered){
17508             var cs = this.childNodes;
17509             for(var i = 0, len = cs.length; i < len; i++){
17510                 cs[i].render(true);
17511             }
17512         }
17513     },
17514
17515     // private
17516     render : function(bulkRender){
17517         this.ui.render(bulkRender);
17518         if(!this.rendered){
17519             this.rendered = true;
17520             if(this.expanded){
17521                 this.expanded = false;
17522                 this.expand(false, false);
17523             }
17524         }
17525     },
17526
17527     // private
17528     renderIndent : function(deep, refresh){
17529         if(refresh){
17530             this.ui.childIndent = null;
17531         }
17532         this.ui.renderIndent();
17533         if(deep === true && this.childrenRendered){
17534             var cs = this.childNodes;
17535             for(var i = 0, len = cs.length; i < len; i++){
17536                 cs[i].renderIndent(true, refresh);
17537             }
17538         }
17539     }
17540 });/*
17541  * Based on:
17542  * Ext JS Library 1.1.1
17543  * Copyright(c) 2006-2007, Ext JS, LLC.
17544  *
17545  * Originally Released Under LGPL - original licence link has changed is not relivant.
17546  *
17547  * Fork - LGPL
17548  * <script type="text/javascript">
17549  */
17550  
17551 /**
17552  * @class Roo.tree.AsyncTreeNode
17553  * @extends Roo.tree.TreeNode
17554  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17555  * @constructor
17556  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17557  */
17558  Roo.tree.AsyncTreeNode = function(config){
17559     this.loaded = false;
17560     this.loading = false;
17561     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17562     /**
17563     * @event beforeload
17564     * Fires before this node is loaded, return false to cancel
17565     * @param {Node} this This node
17566     */
17567     this.addEvents({'beforeload':true, 'load': true});
17568     /**
17569     * @event load
17570     * Fires when this node is loaded
17571     * @param {Node} this This node
17572     */
17573     /**
17574      * The loader used by this node (defaults to using the tree's defined loader)
17575      * @type TreeLoader
17576      * @property loader
17577      */
17578 };
17579 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17580     expand : function(deep, anim, callback){
17581         if(this.loading){ // if an async load is already running, waiting til it's done
17582             var timer;
17583             var f = function(){
17584                 if(!this.loading){ // done loading
17585                     clearInterval(timer);
17586                     this.expand(deep, anim, callback);
17587                 }
17588             }.createDelegate(this);
17589             timer = setInterval(f, 200);
17590             return;
17591         }
17592         if(!this.loaded){
17593             if(this.fireEvent("beforeload", this) === false){
17594                 return;
17595             }
17596             this.loading = true;
17597             this.ui.beforeLoad(this);
17598             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17599             if(loader){
17600                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17601                 return;
17602             }
17603         }
17604         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17605     },
17606     
17607     /**
17608      * Returns true if this node is currently loading
17609      * @return {Boolean}
17610      */
17611     isLoading : function(){
17612         return this.loading;  
17613     },
17614     
17615     loadComplete : function(deep, anim, callback){
17616         this.loading = false;
17617         this.loaded = true;
17618         this.ui.afterLoad(this);
17619         this.fireEvent("load", this);
17620         this.expand(deep, anim, callback);
17621     },
17622     
17623     /**
17624      * Returns true if this node has been loaded
17625      * @return {Boolean}
17626      */
17627     isLoaded : function(){
17628         return this.loaded;
17629     },
17630     
17631     hasChildNodes : function(){
17632         if(!this.isLeaf() && !this.loaded){
17633             return true;
17634         }else{
17635             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17636         }
17637     },
17638
17639     /**
17640      * Trigger a reload for this node
17641      * @param {Function} callback
17642      */
17643     reload : function(callback){
17644         this.collapse(false, false);
17645         while(this.firstChild){
17646             this.removeChild(this.firstChild);
17647         }
17648         this.childrenRendered = false;
17649         this.loaded = false;
17650         if(this.isHiddenRoot()){
17651             this.expanded = false;
17652         }
17653         this.expand(false, false, callback);
17654     }
17655 });/*
17656  * Based on:
17657  * Ext JS Library 1.1.1
17658  * Copyright(c) 2006-2007, Ext JS, LLC.
17659  *
17660  * Originally Released Under LGPL - original licence link has changed is not relivant.
17661  *
17662  * Fork - LGPL
17663  * <script type="text/javascript">
17664  */
17665  
17666 /**
17667  * @class Roo.tree.TreeNodeUI
17668  * @constructor
17669  * @param {Object} node The node to render
17670  * The TreeNode UI implementation is separate from the
17671  * tree implementation. Unless you are customizing the tree UI,
17672  * you should never have to use this directly.
17673  */
17674 Roo.tree.TreeNodeUI = function(node){
17675     this.node = node;
17676     this.rendered = false;
17677     this.animating = false;
17678     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17679 };
17680
17681 Roo.tree.TreeNodeUI.prototype = {
17682     removeChild : function(node){
17683         if(this.rendered){
17684             this.ctNode.removeChild(node.ui.getEl());
17685         }
17686     },
17687
17688     beforeLoad : function(){
17689          this.addClass("x-tree-node-loading");
17690     },
17691
17692     afterLoad : function(){
17693          this.removeClass("x-tree-node-loading");
17694     },
17695
17696     onTextChange : function(node, text, oldText){
17697         if(this.rendered){
17698             this.textNode.innerHTML = text;
17699         }
17700     },
17701
17702     onDisableChange : function(node, state){
17703         this.disabled = state;
17704         if(state){
17705             this.addClass("x-tree-node-disabled");
17706         }else{
17707             this.removeClass("x-tree-node-disabled");
17708         }
17709     },
17710
17711     onSelectedChange : function(state){
17712         if(state){
17713             this.focus();
17714             this.addClass("x-tree-selected");
17715         }else{
17716             //this.blur();
17717             this.removeClass("x-tree-selected");
17718         }
17719     },
17720
17721     onMove : function(tree, node, oldParent, newParent, index, refNode){
17722         this.childIndent = null;
17723         if(this.rendered){
17724             var targetNode = newParent.ui.getContainer();
17725             if(!targetNode){//target not rendered
17726                 this.holder = document.createElement("div");
17727                 this.holder.appendChild(this.wrap);
17728                 return;
17729             }
17730             var insertBefore = refNode ? refNode.ui.getEl() : null;
17731             if(insertBefore){
17732                 targetNode.insertBefore(this.wrap, insertBefore);
17733             }else{
17734                 targetNode.appendChild(this.wrap);
17735             }
17736             this.node.renderIndent(true);
17737         }
17738     },
17739
17740     addClass : function(cls){
17741         if(this.elNode){
17742             Roo.fly(this.elNode).addClass(cls);
17743         }
17744     },
17745
17746     removeClass : function(cls){
17747         if(this.elNode){
17748             Roo.fly(this.elNode).removeClass(cls);
17749         }
17750     },
17751
17752     remove : function(){
17753         if(this.rendered){
17754             this.holder = document.createElement("div");
17755             this.holder.appendChild(this.wrap);
17756         }
17757     },
17758
17759     fireEvent : function(){
17760         return this.node.fireEvent.apply(this.node, arguments);
17761     },
17762
17763     initEvents : function(){
17764         this.node.on("move", this.onMove, this);
17765         var E = Roo.EventManager;
17766         var a = this.anchor;
17767
17768         var el = Roo.fly(a, '_treeui');
17769
17770         if(Roo.isOpera){ // opera render bug ignores the CSS
17771             el.setStyle("text-decoration", "none");
17772         }
17773
17774         el.on("click", this.onClick, this);
17775         el.on("dblclick", this.onDblClick, this);
17776
17777         if(this.checkbox){
17778             Roo.EventManager.on(this.checkbox,
17779                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17780         }
17781
17782         el.on("contextmenu", this.onContextMenu, this);
17783
17784         var icon = Roo.fly(this.iconNode);
17785         icon.on("click", this.onClick, this);
17786         icon.on("dblclick", this.onDblClick, this);
17787         icon.on("contextmenu", this.onContextMenu, this);
17788         E.on(this.ecNode, "click", this.ecClick, this, true);
17789
17790         if(this.node.disabled){
17791             this.addClass("x-tree-node-disabled");
17792         }
17793         if(this.node.hidden){
17794             this.addClass("x-tree-node-disabled");
17795         }
17796         var ot = this.node.getOwnerTree();
17797         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17798         if(dd && (!this.node.isRoot || ot.rootVisible)){
17799             Roo.dd.Registry.register(this.elNode, {
17800                 node: this.node,
17801                 handles: this.getDDHandles(),
17802                 isHandle: false
17803             });
17804         }
17805     },
17806
17807     getDDHandles : function(){
17808         return [this.iconNode, this.textNode];
17809     },
17810
17811     hide : function(){
17812         if(this.rendered){
17813             this.wrap.style.display = "none";
17814         }
17815     },
17816
17817     show : function(){
17818         if(this.rendered){
17819             this.wrap.style.display = "";
17820         }
17821     },
17822
17823     onContextMenu : function(e){
17824         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17825             e.preventDefault();
17826             this.focus();
17827             this.fireEvent("contextmenu", this.node, e);
17828         }
17829     },
17830
17831     onClick : function(e){
17832         if(this.dropping){
17833             e.stopEvent();
17834             return;
17835         }
17836         if(this.fireEvent("beforeclick", this.node, e) !== false){
17837             if(!this.disabled && this.node.attributes.href){
17838                 this.fireEvent("click", this.node, e);
17839                 return;
17840             }
17841             e.preventDefault();
17842             if(this.disabled){
17843                 return;
17844             }
17845
17846             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17847                 this.node.toggle();
17848             }
17849
17850             this.fireEvent("click", this.node, e);
17851         }else{
17852             e.stopEvent();
17853         }
17854     },
17855
17856     onDblClick : function(e){
17857         e.preventDefault();
17858         if(this.disabled){
17859             return;
17860         }
17861         if(this.checkbox){
17862             this.toggleCheck();
17863         }
17864         if(!this.animating && this.node.hasChildNodes()){
17865             this.node.toggle();
17866         }
17867         this.fireEvent("dblclick", this.node, e);
17868     },
17869
17870     onCheckChange : function(){
17871         var checked = this.checkbox.checked;
17872         this.node.attributes.checked = checked;
17873         this.fireEvent('checkchange', this.node, checked);
17874     },
17875
17876     ecClick : function(e){
17877         if(!this.animating && this.node.hasChildNodes()){
17878             this.node.toggle();
17879         }
17880     },
17881
17882     startDrop : function(){
17883         this.dropping = true;
17884     },
17885
17886     // delayed drop so the click event doesn't get fired on a drop
17887     endDrop : function(){
17888        setTimeout(function(){
17889            this.dropping = false;
17890        }.createDelegate(this), 50);
17891     },
17892
17893     expand : function(){
17894         this.updateExpandIcon();
17895         this.ctNode.style.display = "";
17896     },
17897
17898     focus : function(){
17899         if(!this.node.preventHScroll){
17900             try{this.anchor.focus();
17901             }catch(e){}
17902         }else if(!Roo.isIE){
17903             try{
17904                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17905                 var l = noscroll.scrollLeft;
17906                 this.anchor.focus();
17907                 noscroll.scrollLeft = l;
17908             }catch(e){}
17909         }
17910     },
17911
17912     toggleCheck : function(value){
17913         var cb = this.checkbox;
17914         if(cb){
17915             cb.checked = (value === undefined ? !cb.checked : value);
17916         }
17917     },
17918
17919     blur : function(){
17920         try{
17921             this.anchor.blur();
17922         }catch(e){}
17923     },
17924
17925     animExpand : function(callback){
17926         var ct = Roo.get(this.ctNode);
17927         ct.stopFx();
17928         if(!this.node.hasChildNodes()){
17929             this.updateExpandIcon();
17930             this.ctNode.style.display = "";
17931             Roo.callback(callback);
17932             return;
17933         }
17934         this.animating = true;
17935         this.updateExpandIcon();
17936
17937         ct.slideIn('t', {
17938            callback : function(){
17939                this.animating = false;
17940                Roo.callback(callback);
17941             },
17942             scope: this,
17943             duration: this.node.ownerTree.duration || .25
17944         });
17945     },
17946
17947     highlight : function(){
17948         var tree = this.node.getOwnerTree();
17949         Roo.fly(this.wrap).highlight(
17950             tree.hlColor || "C3DAF9",
17951             {endColor: tree.hlBaseColor}
17952         );
17953     },
17954
17955     collapse : function(){
17956         this.updateExpandIcon();
17957         this.ctNode.style.display = "none";
17958     },
17959
17960     animCollapse : function(callback){
17961         var ct = Roo.get(this.ctNode);
17962         ct.enableDisplayMode('block');
17963         ct.stopFx();
17964
17965         this.animating = true;
17966         this.updateExpandIcon();
17967
17968         ct.slideOut('t', {
17969             callback : function(){
17970                this.animating = false;
17971                Roo.callback(callback);
17972             },
17973             scope: this,
17974             duration: this.node.ownerTree.duration || .25
17975         });
17976     },
17977
17978     getContainer : function(){
17979         return this.ctNode;
17980     },
17981
17982     getEl : function(){
17983         return this.wrap;
17984     },
17985
17986     appendDDGhost : function(ghostNode){
17987         ghostNode.appendChild(this.elNode.cloneNode(true));
17988     },
17989
17990     getDDRepairXY : function(){
17991         return Roo.lib.Dom.getXY(this.iconNode);
17992     },
17993
17994     onRender : function(){
17995         this.render();
17996     },
17997
17998     render : function(bulkRender){
17999         var n = this.node, a = n.attributes;
18000         var targetNode = n.parentNode ?
18001               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18002
18003         if(!this.rendered){
18004             this.rendered = true;
18005
18006             this.renderElements(n, a, targetNode, bulkRender);
18007
18008             if(a.qtip){
18009                if(this.textNode.setAttributeNS){
18010                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18011                    if(a.qtipTitle){
18012                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18013                    }
18014                }else{
18015                    this.textNode.setAttribute("ext:qtip", a.qtip);
18016                    if(a.qtipTitle){
18017                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18018                    }
18019                }
18020             }else if(a.qtipCfg){
18021                 a.qtipCfg.target = Roo.id(this.textNode);
18022                 Roo.QuickTips.register(a.qtipCfg);
18023             }
18024             this.initEvents();
18025             if(!this.node.expanded){
18026                 this.updateExpandIcon();
18027             }
18028         }else{
18029             if(bulkRender === true) {
18030                 targetNode.appendChild(this.wrap);
18031             }
18032         }
18033     },
18034
18035     renderElements : function(n, a, targetNode, bulkRender){
18036         // add some indent caching, this helps performance when rendering a large tree
18037         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18038         var t = n.getOwnerTree();
18039         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18040         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18041         var cb = typeof a.checked == 'boolean';
18042         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18043         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18044             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18045             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18046             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18047             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18048             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18049              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18050                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18051             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18052             "</li>"];
18053
18054         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18055             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18056                                 n.nextSibling.ui.getEl(), buf.join(""));
18057         }else{
18058             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18059         }
18060
18061         this.elNode = this.wrap.childNodes[0];
18062         this.ctNode = this.wrap.childNodes[1];
18063         var cs = this.elNode.childNodes;
18064         this.indentNode = cs[0];
18065         this.ecNode = cs[1];
18066         this.iconNode = cs[2];
18067         var index = 3;
18068         if(cb){
18069             this.checkbox = cs[3];
18070             index++;
18071         }
18072         this.anchor = cs[index];
18073         this.textNode = cs[index].firstChild;
18074     },
18075
18076     getAnchor : function(){
18077         return this.anchor;
18078     },
18079
18080     getTextEl : function(){
18081         return this.textNode;
18082     },
18083
18084     getIconEl : function(){
18085         return this.iconNode;
18086     },
18087
18088     isChecked : function(){
18089         return this.checkbox ? this.checkbox.checked : false;
18090     },
18091
18092     updateExpandIcon : function(){
18093         if(this.rendered){
18094             var n = this.node, c1, c2;
18095             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18096             var hasChild = n.hasChildNodes();
18097             if(hasChild){
18098                 if(n.expanded){
18099                     cls += "-minus";
18100                     c1 = "x-tree-node-collapsed";
18101                     c2 = "x-tree-node-expanded";
18102                 }else{
18103                     cls += "-plus";
18104                     c1 = "x-tree-node-expanded";
18105                     c2 = "x-tree-node-collapsed";
18106                 }
18107                 if(this.wasLeaf){
18108                     this.removeClass("x-tree-node-leaf");
18109                     this.wasLeaf = false;
18110                 }
18111                 if(this.c1 != c1 || this.c2 != c2){
18112                     Roo.fly(this.elNode).replaceClass(c1, c2);
18113                     this.c1 = c1; this.c2 = c2;
18114                 }
18115             }else{
18116                 if(!this.wasLeaf){
18117                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18118                     delete this.c1;
18119                     delete this.c2;
18120                     this.wasLeaf = true;
18121                 }
18122             }
18123             var ecc = "x-tree-ec-icon "+cls;
18124             if(this.ecc != ecc){
18125                 this.ecNode.className = ecc;
18126                 this.ecc = ecc;
18127             }
18128         }
18129     },
18130
18131     getChildIndent : function(){
18132         if(!this.childIndent){
18133             var buf = [];
18134             var p = this.node;
18135             while(p){
18136                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18137                     if(!p.isLast()) {
18138                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18139                     } else {
18140                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18141                     }
18142                 }
18143                 p = p.parentNode;
18144             }
18145             this.childIndent = buf.join("");
18146         }
18147         return this.childIndent;
18148     },
18149
18150     renderIndent : function(){
18151         if(this.rendered){
18152             var indent = "";
18153             var p = this.node.parentNode;
18154             if(p){
18155                 indent = p.ui.getChildIndent();
18156             }
18157             if(this.indentMarkup != indent){ // don't rerender if not required
18158                 this.indentNode.innerHTML = indent;
18159                 this.indentMarkup = indent;
18160             }
18161             this.updateExpandIcon();
18162         }
18163     }
18164 };
18165
18166 Roo.tree.RootTreeNodeUI = function(){
18167     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18168 };
18169 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18170     render : function(){
18171         if(!this.rendered){
18172             var targetNode = this.node.ownerTree.innerCt.dom;
18173             this.node.expanded = true;
18174             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18175             this.wrap = this.ctNode = targetNode.firstChild;
18176         }
18177     },
18178     collapse : function(){
18179     },
18180     expand : function(){
18181     }
18182 });/*
18183  * Based on:
18184  * Ext JS Library 1.1.1
18185  * Copyright(c) 2006-2007, Ext JS, LLC.
18186  *
18187  * Originally Released Under LGPL - original licence link has changed is not relivant.
18188  *
18189  * Fork - LGPL
18190  * <script type="text/javascript">
18191  */
18192 /**
18193  * @class Roo.tree.TreeLoader
18194  * @extends Roo.util.Observable
18195  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18196  * nodes from a specified URL. The response must be a javascript Array definition
18197  * who's elements are node definition objects. eg:
18198  * <pre><code>
18199    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18200     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18201 </code></pre>
18202  * <br><br>
18203  * A server request is sent, and child nodes are loaded only when a node is expanded.
18204  * The loading node's id is passed to the server under the parameter name "node" to
18205  * enable the server to produce the correct child nodes.
18206  * <br><br>
18207  * To pass extra parameters, an event handler may be attached to the "beforeload"
18208  * event, and the parameters specified in the TreeLoader's baseParams property:
18209  * <pre><code>
18210     myTreeLoader.on("beforeload", function(treeLoader, node) {
18211         this.baseParams.category = node.attributes.category;
18212     }, this);
18213 </code></pre><
18214  * This would pass an HTTP parameter called "category" to the server containing
18215  * the value of the Node's "category" attribute.
18216  * @constructor
18217  * Creates a new Treeloader.
18218  * @param {Object} config A config object containing config properties.
18219  */
18220 Roo.tree.TreeLoader = function(config){
18221     this.baseParams = {};
18222     this.requestMethod = "POST";
18223     Roo.apply(this, config);
18224
18225     this.addEvents({
18226     
18227         /**
18228          * @event beforeload
18229          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18230          * @param {Object} This TreeLoader object.
18231          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18232          * @param {Object} callback The callback function specified in the {@link #load} call.
18233          */
18234         beforeload : true,
18235         /**
18236          * @event load
18237          * Fires when the node has been successfuly loaded.
18238          * @param {Object} This TreeLoader object.
18239          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18240          * @param {Object} response The response object containing the data from the server.
18241          */
18242         load : true,
18243         /**
18244          * @event loadexception
18245          * Fires if the network request failed.
18246          * @param {Object} This TreeLoader object.
18247          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18248          * @param {Object} response The response object containing the data from the server.
18249          */
18250         loadexception : true,
18251         /**
18252          * @event create
18253          * Fires before a node is created, enabling you to return custom Node types 
18254          * @param {Object} This TreeLoader object.
18255          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18256          */
18257         create : true
18258     });
18259
18260     Roo.tree.TreeLoader.superclass.constructor.call(this);
18261 };
18262
18263 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18264     /**
18265     * @cfg {String} dataUrl The URL from which to request a Json string which
18266     * specifies an array of node definition object representing the child nodes
18267     * to be loaded.
18268     */
18269     /**
18270     * @cfg {Object} baseParams (optional) An object containing properties which
18271     * specify HTTP parameters to be passed to each request for child nodes.
18272     */
18273     /**
18274     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18275     * created by this loader. If the attributes sent by the server have an attribute in this object,
18276     * they take priority.
18277     */
18278     /**
18279     * @cfg {Object} uiProviders (optional) An object containing properties which
18280     * 
18281     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18282     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18283     * <i>uiProvider</i> attribute of a returned child node is a string rather
18284     * than a reference to a TreeNodeUI implementation, this that string value
18285     * is used as a property name in the uiProviders object. You can define the provider named
18286     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18287     */
18288     uiProviders : {},
18289
18290     /**
18291     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18292     * child nodes before loading.
18293     */
18294     clearOnLoad : true,
18295
18296     /**
18297     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18298     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18299     * Grid query { data : [ .....] }
18300     */
18301     
18302     root : false,
18303      /**
18304     * @cfg {String} queryParam (optional) 
18305     * Name of the query as it will be passed on the querystring (defaults to 'node')
18306     * eg. the request will be ?node=[id]
18307     */
18308     
18309     
18310     queryParam: false,
18311     
18312     /**
18313      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18314      * This is called automatically when a node is expanded, but may be used to reload
18315      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18316      * @param {Roo.tree.TreeNode} node
18317      * @param {Function} callback
18318      */
18319     load : function(node, callback){
18320         if(this.clearOnLoad){
18321             while(node.firstChild){
18322                 node.removeChild(node.firstChild);
18323             }
18324         }
18325         if(node.attributes.children){ // preloaded json children
18326             var cs = node.attributes.children;
18327             for(var i = 0, len = cs.length; i < len; i++){
18328                 node.appendChild(this.createNode(cs[i]));
18329             }
18330             if(typeof callback == "function"){
18331                 callback();
18332             }
18333         }else if(this.dataUrl){
18334             this.requestData(node, callback);
18335         }
18336     },
18337
18338     getParams: function(node){
18339         var buf = [], bp = this.baseParams;
18340         for(var key in bp){
18341             if(typeof bp[key] != "function"){
18342                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18343             }
18344         }
18345         var n = this.queryParam === false ? 'node' : this.queryParam;
18346         buf.push(n + "=", encodeURIComponent(node.id));
18347         return buf.join("");
18348     },
18349
18350     requestData : function(node, callback){
18351         if(this.fireEvent("beforeload", this, node, callback) !== false){
18352             this.transId = Roo.Ajax.request({
18353                 method:this.requestMethod,
18354                 url: this.dataUrl||this.url,
18355                 success: this.handleResponse,
18356                 failure: this.handleFailure,
18357                 scope: this,
18358                 argument: {callback: callback, node: node},
18359                 params: this.getParams(node)
18360             });
18361         }else{
18362             // if the load is cancelled, make sure we notify
18363             // the node that we are done
18364             if(typeof callback == "function"){
18365                 callback();
18366             }
18367         }
18368     },
18369
18370     isLoading : function(){
18371         return this.transId ? true : false;
18372     },
18373
18374     abort : function(){
18375         if(this.isLoading()){
18376             Roo.Ajax.abort(this.transId);
18377         }
18378     },
18379
18380     // private
18381     createNode : function(attr){
18382         // apply baseAttrs, nice idea Corey!
18383         if(this.baseAttrs){
18384             Roo.applyIf(attr, this.baseAttrs);
18385         }
18386         if(this.applyLoader !== false){
18387             attr.loader = this;
18388         }
18389         // uiProvider = depreciated..
18390         
18391         if(typeof(attr.uiProvider) == 'string'){
18392            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18393                 /**  eval:var:attr */ eval(attr.uiProvider);
18394         }
18395         if(typeof(this.uiProviders['default']) != 'undefined') {
18396             attr.uiProvider = this.uiProviders['default'];
18397         }
18398         
18399         this.fireEvent('create', this, attr);
18400         
18401         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18402         return(attr.leaf ?
18403                         new Roo.tree.TreeNode(attr) :
18404                         new Roo.tree.AsyncTreeNode(attr));
18405     },
18406
18407     processResponse : function(response, node, callback){
18408         var json = response.responseText;
18409         try {
18410             
18411             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18412             if (this.root !== false) {
18413                 o = o[this.root];
18414             }
18415             
18416             for(var i = 0, len = o.length; i < len; i++){
18417                 var n = this.createNode(o[i]);
18418                 if(n){
18419                     node.appendChild(n);
18420                 }
18421             }
18422             if(typeof callback == "function"){
18423                 callback(this, node);
18424             }
18425         }catch(e){
18426             this.handleFailure(response);
18427         }
18428     },
18429
18430     handleResponse : function(response){
18431         this.transId = false;
18432         var a = response.argument;
18433         this.processResponse(response, a.node, a.callback);
18434         this.fireEvent("load", this, a.node, response);
18435     },
18436
18437     handleFailure : function(response){
18438         this.transId = false;
18439         var a = response.argument;
18440         this.fireEvent("loadexception", this, a.node, response);
18441         if(typeof a.callback == "function"){
18442             a.callback(this, a.node);
18443         }
18444     }
18445 });/*
18446  * Based on:
18447  * Ext JS Library 1.1.1
18448  * Copyright(c) 2006-2007, Ext JS, LLC.
18449  *
18450  * Originally Released Under LGPL - original licence link has changed is not relivant.
18451  *
18452  * Fork - LGPL
18453  * <script type="text/javascript">
18454  */
18455
18456 /**
18457 * @class Roo.tree.TreeFilter
18458 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18459 * @param {TreePanel} tree
18460 * @param {Object} config (optional)
18461  */
18462 Roo.tree.TreeFilter = function(tree, config){
18463     this.tree = tree;
18464     this.filtered = {};
18465     Roo.apply(this, config);
18466 };
18467
18468 Roo.tree.TreeFilter.prototype = {
18469     clearBlank:false,
18470     reverse:false,
18471     autoClear:false,
18472     remove:false,
18473
18474      /**
18475      * Filter the data by a specific attribute.
18476      * @param {String/RegExp} value Either string that the attribute value
18477      * should start with or a RegExp to test against the attribute
18478      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18479      * @param {TreeNode} startNode (optional) The node to start the filter at.
18480      */
18481     filter : function(value, attr, startNode){
18482         attr = attr || "text";
18483         var f;
18484         if(typeof value == "string"){
18485             var vlen = value.length;
18486             // auto clear empty filter
18487             if(vlen == 0 && this.clearBlank){
18488                 this.clear();
18489                 return;
18490             }
18491             value = value.toLowerCase();
18492             f = function(n){
18493                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18494             };
18495         }else if(value.exec){ // regex?
18496             f = function(n){
18497                 return value.test(n.attributes[attr]);
18498             };
18499         }else{
18500             throw 'Illegal filter type, must be string or regex';
18501         }
18502         this.filterBy(f, null, startNode);
18503         },
18504
18505     /**
18506      * Filter by a function. The passed function will be called with each
18507      * node in the tree (or from the startNode). If the function returns true, the node is kept
18508      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18509      * @param {Function} fn The filter function
18510      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18511      */
18512     filterBy : function(fn, scope, startNode){
18513         startNode = startNode || this.tree.root;
18514         if(this.autoClear){
18515             this.clear();
18516         }
18517         var af = this.filtered, rv = this.reverse;
18518         var f = function(n){
18519             if(n == startNode){
18520                 return true;
18521             }
18522             if(af[n.id]){
18523                 return false;
18524             }
18525             var m = fn.call(scope || n, n);
18526             if(!m || rv){
18527                 af[n.id] = n;
18528                 n.ui.hide();
18529                 return false;
18530             }
18531             return true;
18532         };
18533         startNode.cascade(f);
18534         if(this.remove){
18535            for(var id in af){
18536                if(typeof id != "function"){
18537                    var n = af[id];
18538                    if(n && n.parentNode){
18539                        n.parentNode.removeChild(n);
18540                    }
18541                }
18542            }
18543         }
18544     },
18545
18546     /**
18547      * Clears the current filter. Note: with the "remove" option
18548      * set a filter cannot be cleared.
18549      */
18550     clear : function(){
18551         var t = this.tree;
18552         var af = this.filtered;
18553         for(var id in af){
18554             if(typeof id != "function"){
18555                 var n = af[id];
18556                 if(n){
18557                     n.ui.show();
18558                 }
18559             }
18560         }
18561         this.filtered = {};
18562     }
18563 };
18564 /*
18565  * Based on:
18566  * Ext JS Library 1.1.1
18567  * Copyright(c) 2006-2007, Ext JS, LLC.
18568  *
18569  * Originally Released Under LGPL - original licence link has changed is not relivant.
18570  *
18571  * Fork - LGPL
18572  * <script type="text/javascript">
18573  */
18574  
18575
18576 /**
18577  * @class Roo.tree.TreeSorter
18578  * Provides sorting of nodes in a TreePanel
18579  * 
18580  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18581  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18582  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18583  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18584  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18585  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18586  * @constructor
18587  * @param {TreePanel} tree
18588  * @param {Object} config
18589  */
18590 Roo.tree.TreeSorter = function(tree, config){
18591     Roo.apply(this, config);
18592     tree.on("beforechildrenrendered", this.doSort, this);
18593     tree.on("append", this.updateSort, this);
18594     tree.on("insert", this.updateSort, this);
18595     
18596     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18597     var p = this.property || "text";
18598     var sortType = this.sortType;
18599     var fs = this.folderSort;
18600     var cs = this.caseSensitive === true;
18601     var leafAttr = this.leafAttr || 'leaf';
18602
18603     this.sortFn = function(n1, n2){
18604         if(fs){
18605             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18606                 return 1;
18607             }
18608             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18609                 return -1;
18610             }
18611         }
18612         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18613         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18614         if(v1 < v2){
18615                         return dsc ? +1 : -1;
18616                 }else if(v1 > v2){
18617                         return dsc ? -1 : +1;
18618         }else{
18619                 return 0;
18620         }
18621     };
18622 };
18623
18624 Roo.tree.TreeSorter.prototype = {
18625     doSort : function(node){
18626         node.sort(this.sortFn);
18627     },
18628     
18629     compareNodes : function(n1, n2){
18630         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18631     },
18632     
18633     updateSort : function(tree, node){
18634         if(node.childrenRendered){
18635             this.doSort.defer(1, this, [node]);
18636         }
18637     }
18638 };/*
18639  * Based on:
18640  * Ext JS Library 1.1.1
18641  * Copyright(c) 2006-2007, Ext JS, LLC.
18642  *
18643  * Originally Released Under LGPL - original licence link has changed is not relivant.
18644  *
18645  * Fork - LGPL
18646  * <script type="text/javascript">
18647  */
18648
18649 if(Roo.dd.DropZone){
18650     
18651 Roo.tree.TreeDropZone = function(tree, config){
18652     this.allowParentInsert = false;
18653     this.allowContainerDrop = false;
18654     this.appendOnly = false;
18655     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18656     this.tree = tree;
18657     this.lastInsertClass = "x-tree-no-status";
18658     this.dragOverData = {};
18659 };
18660
18661 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18662     ddGroup : "TreeDD",
18663     
18664     expandDelay : 1000,
18665     
18666     expandNode : function(node){
18667         if(node.hasChildNodes() && !node.isExpanded()){
18668             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18669         }
18670     },
18671     
18672     queueExpand : function(node){
18673         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18674     },
18675     
18676     cancelExpand : function(){
18677         if(this.expandProcId){
18678             clearTimeout(this.expandProcId);
18679             this.expandProcId = false;
18680         }
18681     },
18682     
18683     isValidDropPoint : function(n, pt, dd, e, data){
18684         if(!n || !data){ return false; }
18685         var targetNode = n.node;
18686         var dropNode = data.node;
18687         // default drop rules
18688         if(!(targetNode && targetNode.isTarget && pt)){
18689             return false;
18690         }
18691         if(pt == "append" && targetNode.allowChildren === false){
18692             return false;
18693         }
18694         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18695             return false;
18696         }
18697         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18698             return false;
18699         }
18700         // reuse the object
18701         var overEvent = this.dragOverData;
18702         overEvent.tree = this.tree;
18703         overEvent.target = targetNode;
18704         overEvent.data = data;
18705         overEvent.point = pt;
18706         overEvent.source = dd;
18707         overEvent.rawEvent = e;
18708         overEvent.dropNode = dropNode;
18709         overEvent.cancel = false;  
18710         var result = this.tree.fireEvent("nodedragover", overEvent);
18711         return overEvent.cancel === false && result !== false;
18712     },
18713     
18714     getDropPoint : function(e, n, dd){
18715         var tn = n.node;
18716         if(tn.isRoot){
18717             return tn.allowChildren !== false ? "append" : false; // always append for root
18718         }
18719         var dragEl = n.ddel;
18720         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18721         var y = Roo.lib.Event.getPageY(e);
18722         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18723         
18724         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18725         var noAppend = tn.allowChildren === false;
18726         if(this.appendOnly || tn.parentNode.allowChildren === false){
18727             return noAppend ? false : "append";
18728         }
18729         var noBelow = false;
18730         if(!this.allowParentInsert){
18731             noBelow = tn.hasChildNodes() && tn.isExpanded();
18732         }
18733         var q = (b - t) / (noAppend ? 2 : 3);
18734         if(y >= t && y < (t + q)){
18735             return "above";
18736         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18737             return "below";
18738         }else{
18739             return "append";
18740         }
18741     },
18742     
18743     onNodeEnter : function(n, dd, e, data){
18744         this.cancelExpand();
18745     },
18746     
18747     onNodeOver : function(n, dd, e, data){
18748         var pt = this.getDropPoint(e, n, dd);
18749         var node = n.node;
18750         
18751         // auto node expand check
18752         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18753             this.queueExpand(node);
18754         }else if(pt != "append"){
18755             this.cancelExpand();
18756         }
18757         
18758         // set the insert point style on the target node
18759         var returnCls = this.dropNotAllowed;
18760         if(this.isValidDropPoint(n, pt, dd, e, data)){
18761            if(pt){
18762                var el = n.ddel;
18763                var cls;
18764                if(pt == "above"){
18765                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18766                    cls = "x-tree-drag-insert-above";
18767                }else if(pt == "below"){
18768                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18769                    cls = "x-tree-drag-insert-below";
18770                }else{
18771                    returnCls = "x-tree-drop-ok-append";
18772                    cls = "x-tree-drag-append";
18773                }
18774                if(this.lastInsertClass != cls){
18775                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18776                    this.lastInsertClass = cls;
18777                }
18778            }
18779        }
18780        return returnCls;
18781     },
18782     
18783     onNodeOut : function(n, dd, e, data){
18784         this.cancelExpand();
18785         this.removeDropIndicators(n);
18786     },
18787     
18788     onNodeDrop : function(n, dd, e, data){
18789         var point = this.getDropPoint(e, n, dd);
18790         var targetNode = n.node;
18791         targetNode.ui.startDrop();
18792         if(!this.isValidDropPoint(n, point, dd, e, data)){
18793             targetNode.ui.endDrop();
18794             return false;
18795         }
18796         // first try to find the drop node
18797         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18798         var dropEvent = {
18799             tree : this.tree,
18800             target: targetNode,
18801             data: data,
18802             point: point,
18803             source: dd,
18804             rawEvent: e,
18805             dropNode: dropNode,
18806             cancel: !dropNode   
18807         };
18808         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18809         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18810             targetNode.ui.endDrop();
18811             return false;
18812         }
18813         // allow target changing
18814         targetNode = dropEvent.target;
18815         if(point == "append" && !targetNode.isExpanded()){
18816             targetNode.expand(false, null, function(){
18817                 this.completeDrop(dropEvent);
18818             }.createDelegate(this));
18819         }else{
18820             this.completeDrop(dropEvent);
18821         }
18822         return true;
18823     },
18824     
18825     completeDrop : function(de){
18826         var ns = de.dropNode, p = de.point, t = de.target;
18827         if(!(ns instanceof Array)){
18828             ns = [ns];
18829         }
18830         var n;
18831         for(var i = 0, len = ns.length; i < len; i++){
18832             n = ns[i];
18833             if(p == "above"){
18834                 t.parentNode.insertBefore(n, t);
18835             }else if(p == "below"){
18836                 t.parentNode.insertBefore(n, t.nextSibling);
18837             }else{
18838                 t.appendChild(n);
18839             }
18840         }
18841         n.ui.focus();
18842         if(this.tree.hlDrop){
18843             n.ui.highlight();
18844         }
18845         t.ui.endDrop();
18846         this.tree.fireEvent("nodedrop", de);
18847     },
18848     
18849     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18850         if(this.tree.hlDrop){
18851             dropNode.ui.focus();
18852             dropNode.ui.highlight();
18853         }
18854         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18855     },
18856     
18857     getTree : function(){
18858         return this.tree;
18859     },
18860     
18861     removeDropIndicators : function(n){
18862         if(n && n.ddel){
18863             var el = n.ddel;
18864             Roo.fly(el).removeClass([
18865                     "x-tree-drag-insert-above",
18866                     "x-tree-drag-insert-below",
18867                     "x-tree-drag-append"]);
18868             this.lastInsertClass = "_noclass";
18869         }
18870     },
18871     
18872     beforeDragDrop : function(target, e, id){
18873         this.cancelExpand();
18874         return true;
18875     },
18876     
18877     afterRepair : function(data){
18878         if(data && Roo.enableFx){
18879             data.node.ui.highlight();
18880         }
18881         this.hideProxy();
18882     }    
18883 });
18884
18885 }
18886 /*
18887  * Based on:
18888  * Ext JS Library 1.1.1
18889  * Copyright(c) 2006-2007, Ext JS, LLC.
18890  *
18891  * Originally Released Under LGPL - original licence link has changed is not relivant.
18892  *
18893  * Fork - LGPL
18894  * <script type="text/javascript">
18895  */
18896  
18897
18898 if(Roo.dd.DragZone){
18899 Roo.tree.TreeDragZone = function(tree, config){
18900     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18901     this.tree = tree;
18902 };
18903
18904 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18905     ddGroup : "TreeDD",
18906     
18907     onBeforeDrag : function(data, e){
18908         var n = data.node;
18909         return n && n.draggable && !n.disabled;
18910     },
18911     
18912     onInitDrag : function(e){
18913         var data = this.dragData;
18914         this.tree.getSelectionModel().select(data.node);
18915         this.proxy.update("");
18916         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18917         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18918     },
18919     
18920     getRepairXY : function(e, data){
18921         return data.node.ui.getDDRepairXY();
18922     },
18923     
18924     onEndDrag : function(data, e){
18925         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18926     },
18927     
18928     onValidDrop : function(dd, e, id){
18929         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18930         this.hideProxy();
18931     },
18932     
18933     beforeInvalidDrop : function(e, id){
18934         // this scrolls the original position back into view
18935         var sm = this.tree.getSelectionModel();
18936         sm.clearSelections();
18937         sm.select(this.dragData.node);
18938     }
18939 });
18940 }/*
18941  * Based on:
18942  * Ext JS Library 1.1.1
18943  * Copyright(c) 2006-2007, Ext JS, LLC.
18944  *
18945  * Originally Released Under LGPL - original licence link has changed is not relivant.
18946  *
18947  * Fork - LGPL
18948  * <script type="text/javascript">
18949  */
18950 /**
18951  * @class Roo.tree.TreeEditor
18952  * @extends Roo.Editor
18953  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18954  * as the editor field.
18955  * @constructor
18956  * @param {TreePanel} tree
18957  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18958  */
18959 Roo.tree.TreeEditor = function(tree, config){
18960     config = config || {};
18961     var field = config.events ? config : new Roo.form.TextField(config);
18962     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18963
18964     this.tree = tree;
18965
18966     tree.on('beforeclick', this.beforeNodeClick, this);
18967     tree.getTreeEl().on('mousedown', this.hide, this);
18968     this.on('complete', this.updateNode, this);
18969     this.on('beforestartedit', this.fitToTree, this);
18970     this.on('startedit', this.bindScroll, this, {delay:10});
18971     this.on('specialkey', this.onSpecialKey, this);
18972 };
18973
18974 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18975     /**
18976      * @cfg {String} alignment
18977      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18978      */
18979     alignment: "l-l",
18980     // inherit
18981     autoSize: false,
18982     /**
18983      * @cfg {Boolean} hideEl
18984      * True to hide the bound element while the editor is displayed (defaults to false)
18985      */
18986     hideEl : false,
18987     /**
18988      * @cfg {String} cls
18989      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18990      */
18991     cls: "x-small-editor x-tree-editor",
18992     /**
18993      * @cfg {Boolean} shim
18994      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18995      */
18996     shim:false,
18997     // inherit
18998     shadow:"frame",
18999     /**
19000      * @cfg {Number} maxWidth
19001      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19002      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19003      * scroll and client offsets into account prior to each edit.
19004      */
19005     maxWidth: 250,
19006
19007     editDelay : 350,
19008
19009     // private
19010     fitToTree : function(ed, el){
19011         var td = this.tree.getTreeEl().dom, nd = el.dom;
19012         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19013             td.scrollLeft = nd.offsetLeft;
19014         }
19015         var w = Math.min(
19016                 this.maxWidth,
19017                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19018         this.setSize(w, '');
19019     },
19020
19021     // private
19022     triggerEdit : function(node){
19023         this.completeEdit();
19024         this.editNode = node;
19025         this.startEdit(node.ui.textNode, node.text);
19026     },
19027
19028     // private
19029     bindScroll : function(){
19030         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19031     },
19032
19033     // private
19034     beforeNodeClick : function(node, e){
19035         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19036         this.lastClick = new Date();
19037         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19038             e.stopEvent();
19039             this.triggerEdit(node);
19040             return false;
19041         }
19042     },
19043
19044     // private
19045     updateNode : function(ed, value){
19046         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19047         this.editNode.setText(value);
19048     },
19049
19050     // private
19051     onHide : function(){
19052         Roo.tree.TreeEditor.superclass.onHide.call(this);
19053         if(this.editNode){
19054             this.editNode.ui.focus();
19055         }
19056     },
19057
19058     // private
19059     onSpecialKey : function(field, e){
19060         var k = e.getKey();
19061         if(k == e.ESC){
19062             e.stopEvent();
19063             this.cancelEdit();
19064         }else if(k == e.ENTER && !e.hasModifier()){
19065             e.stopEvent();
19066             this.completeEdit();
19067         }
19068     }
19069 });//<Script type="text/javascript">
19070 /*
19071  * Based on:
19072  * Ext JS Library 1.1.1
19073  * Copyright(c) 2006-2007, Ext JS, LLC.
19074  *
19075  * Originally Released Under LGPL - original licence link has changed is not relivant.
19076  *
19077  * Fork - LGPL
19078  * <script type="text/javascript">
19079  */
19080  
19081 /**
19082  * Not documented??? - probably should be...
19083  */
19084
19085 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19086     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19087     
19088     renderElements : function(n, a, targetNode, bulkRender){
19089         //consel.log("renderElements?");
19090         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19091
19092         var t = n.getOwnerTree();
19093         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19094         
19095         var cols = t.columns;
19096         var bw = t.borderWidth;
19097         var c = cols[0];
19098         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19099          var cb = typeof a.checked == "boolean";
19100         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19101         var colcls = 'x-t-' + tid + '-c0';
19102         var buf = [
19103             '<li class="x-tree-node">',
19104             
19105                 
19106                 '<div class="x-tree-node-el ', a.cls,'">',
19107                     // extran...
19108                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19109                 
19110                 
19111                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19112                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19113                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19114                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19115                            (a.iconCls ? ' '+a.iconCls : ''),
19116                            '" unselectable="on" />',
19117                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19118                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19119                              
19120                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19121                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19122                             '<span unselectable="on" qtip="' + tx + '">',
19123                              tx,
19124                              '</span></a>' ,
19125                     '</div>',
19126                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19127                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19128                  ];
19129         for(var i = 1, len = cols.length; i < len; i++){
19130             c = cols[i];
19131             colcls = 'x-t-' + tid + '-c' +i;
19132             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19133             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19134                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19135                       "</div>");
19136          }
19137          
19138          buf.push(
19139             '</a>',
19140             '<div class="x-clear"></div></div>',
19141             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19142             "</li>");
19143         
19144         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19145             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19146                                 n.nextSibling.ui.getEl(), buf.join(""));
19147         }else{
19148             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19149         }
19150         var el = this.wrap.firstChild;
19151         this.elRow = el;
19152         this.elNode = el.firstChild;
19153         this.ranchor = el.childNodes[1];
19154         this.ctNode = this.wrap.childNodes[1];
19155         var cs = el.firstChild.childNodes;
19156         this.indentNode = cs[0];
19157         this.ecNode = cs[1];
19158         this.iconNode = cs[2];
19159         var index = 3;
19160         if(cb){
19161             this.checkbox = cs[3];
19162             index++;
19163         }
19164         this.anchor = cs[index];
19165         
19166         this.textNode = cs[index].firstChild;
19167         
19168         //el.on("click", this.onClick, this);
19169         //el.on("dblclick", this.onDblClick, this);
19170         
19171         
19172        // console.log(this);
19173     },
19174     initEvents : function(){
19175         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19176         
19177             
19178         var a = this.ranchor;
19179
19180         var el = Roo.get(a);
19181
19182         if(Roo.isOpera){ // opera render bug ignores the CSS
19183             el.setStyle("text-decoration", "none");
19184         }
19185
19186         el.on("click", this.onClick, this);
19187         el.on("dblclick", this.onDblClick, this);
19188         el.on("contextmenu", this.onContextMenu, this);
19189         
19190     },
19191     
19192     /*onSelectedChange : function(state){
19193         if(state){
19194             this.focus();
19195             this.addClass("x-tree-selected");
19196         }else{
19197             //this.blur();
19198             this.removeClass("x-tree-selected");
19199         }
19200     },*/
19201     addClass : function(cls){
19202         if(this.elRow){
19203             Roo.fly(this.elRow).addClass(cls);
19204         }
19205         
19206     },
19207     
19208     
19209     removeClass : function(cls){
19210         if(this.elRow){
19211             Roo.fly(this.elRow).removeClass(cls);
19212         }
19213     }
19214
19215     
19216     
19217 });//<Script type="text/javascript">
19218
19219 /*
19220  * Based on:
19221  * Ext JS Library 1.1.1
19222  * Copyright(c) 2006-2007, Ext JS, LLC.
19223  *
19224  * Originally Released Under LGPL - original licence link has changed is not relivant.
19225  *
19226  * Fork - LGPL
19227  * <script type="text/javascript">
19228  */
19229  
19230
19231 /**
19232  * @class Roo.tree.ColumnTree
19233  * @extends Roo.data.TreePanel
19234  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19235  * @cfg {int} borderWidth  compined right/left border allowance
19236  * @constructor
19237  * @param {String/HTMLElement/Element} el The container element
19238  * @param {Object} config
19239  */
19240 Roo.tree.ColumnTree =  function(el, config)
19241 {
19242    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19243    this.addEvents({
19244         /**
19245         * @event resize
19246         * Fire this event on a container when it resizes
19247         * @param {int} w Width
19248         * @param {int} h Height
19249         */
19250        "resize" : true
19251     });
19252     this.on('resize', this.onResize, this);
19253 };
19254
19255 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19256     //lines:false,
19257     
19258     
19259     borderWidth: Roo.isBorderBox ? 0 : 2, 
19260     headEls : false,
19261     
19262     render : function(){
19263         // add the header.....
19264        
19265         Roo.tree.ColumnTree.superclass.render.apply(this);
19266         
19267         this.el.addClass('x-column-tree');
19268         
19269         this.headers = this.el.createChild(
19270             {cls:'x-tree-headers'},this.innerCt.dom);
19271    
19272         var cols = this.columns, c;
19273         var totalWidth = 0;
19274         this.headEls = [];
19275         var  len = cols.length;
19276         for(var i = 0; i < len; i++){
19277              c = cols[i];
19278              totalWidth += c.width;
19279             this.headEls.push(this.headers.createChild({
19280                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19281                  cn: {
19282                      cls:'x-tree-hd-text',
19283                      html: c.header
19284                  },
19285                  style:'width:'+(c.width-this.borderWidth)+'px;'
19286              }));
19287         }
19288         this.headers.createChild({cls:'x-clear'});
19289         // prevent floats from wrapping when clipped
19290         this.headers.setWidth(totalWidth);
19291         //this.innerCt.setWidth(totalWidth);
19292         this.innerCt.setStyle({ overflow: 'auto' });
19293         this.onResize(this.width, this.height);
19294              
19295         
19296     },
19297     onResize : function(w,h)
19298     {
19299         this.height = h;
19300         this.width = w;
19301         // resize cols..
19302         this.innerCt.setWidth(this.width);
19303         this.innerCt.setHeight(this.height-20);
19304         
19305         // headers...
19306         var cols = this.columns, c;
19307         var totalWidth = 0;
19308         var expEl = false;
19309         var len = cols.length;
19310         for(var i = 0; i < len; i++){
19311             c = cols[i];
19312             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19313                 // it's the expander..
19314                 expEl  = this.headEls[i];
19315                 continue;
19316             }
19317             totalWidth += c.width;
19318             
19319         }
19320         if (expEl) {
19321             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19322         }
19323         this.headers.setWidth(w-20);
19324
19325         
19326         
19327         
19328     }
19329 });
19330 /*
19331  * Based on:
19332  * Ext JS Library 1.1.1
19333  * Copyright(c) 2006-2007, Ext JS, LLC.
19334  *
19335  * Originally Released Under LGPL - original licence link has changed is not relivant.
19336  *
19337  * Fork - LGPL
19338  * <script type="text/javascript">
19339  */
19340  
19341 /**
19342  * @class Roo.menu.Menu
19343  * @extends Roo.util.Observable
19344  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19345  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19346  * @constructor
19347  * Creates a new Menu
19348  * @param {Object} config Configuration options
19349  */
19350 Roo.menu.Menu = function(config){
19351     Roo.apply(this, config);
19352     this.id = this.id || Roo.id();
19353     this.addEvents({
19354         /**
19355          * @event beforeshow
19356          * Fires before this menu is displayed
19357          * @param {Roo.menu.Menu} this
19358          */
19359         beforeshow : true,
19360         /**
19361          * @event beforehide
19362          * Fires before this menu is hidden
19363          * @param {Roo.menu.Menu} this
19364          */
19365         beforehide : true,
19366         /**
19367          * @event show
19368          * Fires after this menu is displayed
19369          * @param {Roo.menu.Menu} this
19370          */
19371         show : true,
19372         /**
19373          * @event hide
19374          * Fires after this menu is hidden
19375          * @param {Roo.menu.Menu} this
19376          */
19377         hide : true,
19378         /**
19379          * @event click
19380          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19381          * @param {Roo.menu.Menu} this
19382          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19383          * @param {Roo.EventObject} e
19384          */
19385         click : true,
19386         /**
19387          * @event mouseover
19388          * Fires when the mouse is hovering over this menu
19389          * @param {Roo.menu.Menu} this
19390          * @param {Roo.EventObject} e
19391          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19392          */
19393         mouseover : true,
19394         /**
19395          * @event mouseout
19396          * Fires when the mouse exits this menu
19397          * @param {Roo.menu.Menu} this
19398          * @param {Roo.EventObject} e
19399          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19400          */
19401         mouseout : true,
19402         /**
19403          * @event itemclick
19404          * Fires when a menu item contained in this menu is clicked
19405          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19406          * @param {Roo.EventObject} e
19407          */
19408         itemclick: true
19409     });
19410     if (this.registerMenu) {
19411         Roo.menu.MenuMgr.register(this);
19412     }
19413     
19414     var mis = this.items;
19415     this.items = new Roo.util.MixedCollection();
19416     if(mis){
19417         this.add.apply(this, mis);
19418     }
19419 };
19420
19421 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19422     /**
19423      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19424      */
19425     minWidth : 120,
19426     /**
19427      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19428      * for bottom-right shadow (defaults to "sides")
19429      */
19430     shadow : "sides",
19431     /**
19432      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19433      * this menu (defaults to "tl-tr?")
19434      */
19435     subMenuAlign : "tl-tr?",
19436     /**
19437      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19438      * relative to its element of origin (defaults to "tl-bl?")
19439      */
19440     defaultAlign : "tl-bl?",
19441     /**
19442      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19443      */
19444     allowOtherMenus : false,
19445     /**
19446      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19447      */
19448     registerMenu : true,
19449
19450     hidden:true,
19451
19452     // private
19453     render : function(){
19454         if(this.el){
19455             return;
19456         }
19457         var el = this.el = new Roo.Layer({
19458             cls: "x-menu",
19459             shadow:this.shadow,
19460             constrain: false,
19461             parentEl: this.parentEl || document.body,
19462             zindex:15000
19463         });
19464
19465         this.keyNav = new Roo.menu.MenuNav(this);
19466
19467         if(this.plain){
19468             el.addClass("x-menu-plain");
19469         }
19470         if(this.cls){
19471             el.addClass(this.cls);
19472         }
19473         // generic focus element
19474         this.focusEl = el.createChild({
19475             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19476         });
19477         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19478         ul.on("click", this.onClick, this);
19479         ul.on("mouseover", this.onMouseOver, this);
19480         ul.on("mouseout", this.onMouseOut, this);
19481         this.items.each(function(item){
19482             var li = document.createElement("li");
19483             li.className = "x-menu-list-item";
19484             ul.dom.appendChild(li);
19485             item.render(li, this);
19486         }, this);
19487         this.ul = ul;
19488         this.autoWidth();
19489     },
19490
19491     // private
19492     autoWidth : function(){
19493         var el = this.el, ul = this.ul;
19494         if(!el){
19495             return;
19496         }
19497         var w = this.width;
19498         if(w){
19499             el.setWidth(w);
19500         }else if(Roo.isIE){
19501             el.setWidth(this.minWidth);
19502             var t = el.dom.offsetWidth; // force recalc
19503             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19504         }
19505     },
19506
19507     // private
19508     delayAutoWidth : function(){
19509         if(this.rendered){
19510             if(!this.awTask){
19511                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19512             }
19513             this.awTask.delay(20);
19514         }
19515     },
19516
19517     // private
19518     findTargetItem : function(e){
19519         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19520         if(t && t.menuItemId){
19521             return this.items.get(t.menuItemId);
19522         }
19523     },
19524
19525     // private
19526     onClick : function(e){
19527         var t;
19528         if(t = this.findTargetItem(e)){
19529             t.onClick(e);
19530             this.fireEvent("click", this, t, e);
19531         }
19532     },
19533
19534     // private
19535     setActiveItem : function(item, autoExpand){
19536         if(item != this.activeItem){
19537             if(this.activeItem){
19538                 this.activeItem.deactivate();
19539             }
19540             this.activeItem = item;
19541             item.activate(autoExpand);
19542         }else if(autoExpand){
19543             item.expandMenu();
19544         }
19545     },
19546
19547     // private
19548     tryActivate : function(start, step){
19549         var items = this.items;
19550         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19551             var item = items.get(i);
19552             if(!item.disabled && item.canActivate){
19553                 this.setActiveItem(item, false);
19554                 return item;
19555             }
19556         }
19557         return false;
19558     },
19559
19560     // private
19561     onMouseOver : function(e){
19562         var t;
19563         if(t = this.findTargetItem(e)){
19564             if(t.canActivate && !t.disabled){
19565                 this.setActiveItem(t, true);
19566             }
19567         }
19568         this.fireEvent("mouseover", this, e, t);
19569     },
19570
19571     // private
19572     onMouseOut : function(e){
19573         var t;
19574         if(t = this.findTargetItem(e)){
19575             if(t == this.activeItem && t.shouldDeactivate(e)){
19576                 this.activeItem.deactivate();
19577                 delete this.activeItem;
19578             }
19579         }
19580         this.fireEvent("mouseout", this, e, t);
19581     },
19582
19583     /**
19584      * Read-only.  Returns true if the menu is currently displayed, else false.
19585      * @type Boolean
19586      */
19587     isVisible : function(){
19588         return this.el && !this.hidden;
19589     },
19590
19591     /**
19592      * Displays this menu relative to another element
19593      * @param {String/HTMLElement/Roo.Element} element The element to align to
19594      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19595      * the element (defaults to this.defaultAlign)
19596      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19597      */
19598     show : function(el, pos, parentMenu){
19599         this.parentMenu = parentMenu;
19600         if(!this.el){
19601             this.render();
19602         }
19603         this.fireEvent("beforeshow", this);
19604         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19605     },
19606
19607     /**
19608      * Displays this menu at a specific xy position
19609      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19610      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19611      */
19612     showAt : function(xy, parentMenu, /* private: */_e){
19613         this.parentMenu = parentMenu;
19614         if(!this.el){
19615             this.render();
19616         }
19617         if(_e !== false){
19618             this.fireEvent("beforeshow", this);
19619             xy = this.el.adjustForConstraints(xy);
19620         }
19621         this.el.setXY(xy);
19622         this.el.show();
19623         this.hidden = false;
19624         this.focus();
19625         this.fireEvent("show", this);
19626     },
19627
19628     focus : function(){
19629         if(!this.hidden){
19630             this.doFocus.defer(50, this);
19631         }
19632     },
19633
19634     doFocus : function(){
19635         if(!this.hidden){
19636             this.focusEl.focus();
19637         }
19638     },
19639
19640     /**
19641      * Hides this menu and optionally all parent menus
19642      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19643      */
19644     hide : function(deep){
19645         if(this.el && this.isVisible()){
19646             this.fireEvent("beforehide", this);
19647             if(this.activeItem){
19648                 this.activeItem.deactivate();
19649                 this.activeItem = null;
19650             }
19651             this.el.hide();
19652             this.hidden = true;
19653             this.fireEvent("hide", this);
19654         }
19655         if(deep === true && this.parentMenu){
19656             this.parentMenu.hide(true);
19657         }
19658     },
19659
19660     /**
19661      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19662      * Any of the following are valid:
19663      * <ul>
19664      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19665      * <li>An HTMLElement object which will be converted to a menu item</li>
19666      * <li>A menu item config object that will be created as a new menu item</li>
19667      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19668      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19669      * </ul>
19670      * Usage:
19671      * <pre><code>
19672 // Create the menu
19673 var menu = new Roo.menu.Menu();
19674
19675 // Create a menu item to add by reference
19676 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19677
19678 // Add a bunch of items at once using different methods.
19679 // Only the last item added will be returned.
19680 var item = menu.add(
19681     menuItem,                // add existing item by ref
19682     'Dynamic Item',          // new TextItem
19683     '-',                     // new separator
19684     { text: 'Config Item' }  // new item by config
19685 );
19686 </code></pre>
19687      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19688      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19689      */
19690     add : function(){
19691         var a = arguments, l = a.length, item;
19692         for(var i = 0; i < l; i++){
19693             var el = a[i];
19694             if ((typeof(el) == "object") && el.xtype && el.xns) {
19695                 el = Roo.factory(el, Roo.menu);
19696             }
19697             
19698             if(el.render){ // some kind of Item
19699                 item = this.addItem(el);
19700             }else if(typeof el == "string"){ // string
19701                 if(el == "separator" || el == "-"){
19702                     item = this.addSeparator();
19703                 }else{
19704                     item = this.addText(el);
19705                 }
19706             }else if(el.tagName || el.el){ // element
19707                 item = this.addElement(el);
19708             }else if(typeof el == "object"){ // must be menu item config?
19709                 item = this.addMenuItem(el);
19710             }
19711         }
19712         return item;
19713     },
19714
19715     /**
19716      * Returns this menu's underlying {@link Roo.Element} object
19717      * @return {Roo.Element} The element
19718      */
19719     getEl : function(){
19720         if(!this.el){
19721             this.render();
19722         }
19723         return this.el;
19724     },
19725
19726     /**
19727      * Adds a separator bar to the menu
19728      * @return {Roo.menu.Item} The menu item that was added
19729      */
19730     addSeparator : function(){
19731         return this.addItem(new Roo.menu.Separator());
19732     },
19733
19734     /**
19735      * Adds an {@link Roo.Element} object to the menu
19736      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19737      * @return {Roo.menu.Item} The menu item that was added
19738      */
19739     addElement : function(el){
19740         return this.addItem(new Roo.menu.BaseItem(el));
19741     },
19742
19743     /**
19744      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19745      * @param {Roo.menu.Item} item The menu item to add
19746      * @return {Roo.menu.Item} The menu item that was added
19747      */
19748     addItem : function(item){
19749         this.items.add(item);
19750         if(this.ul){
19751             var li = document.createElement("li");
19752             li.className = "x-menu-list-item";
19753             this.ul.dom.appendChild(li);
19754             item.render(li, this);
19755             this.delayAutoWidth();
19756         }
19757         return item;
19758     },
19759
19760     /**
19761      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19762      * @param {Object} config A MenuItem config object
19763      * @return {Roo.menu.Item} The menu item that was added
19764      */
19765     addMenuItem : function(config){
19766         if(!(config instanceof Roo.menu.Item)){
19767             if(typeof config.checked == "boolean"){ // must be check menu item config?
19768                 config = new Roo.menu.CheckItem(config);
19769             }else{
19770                 config = new Roo.menu.Item(config);
19771             }
19772         }
19773         return this.addItem(config);
19774     },
19775
19776     /**
19777      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19778      * @param {String} text The text to display in the menu item
19779      * @return {Roo.menu.Item} The menu item that was added
19780      */
19781     addText : function(text){
19782         return this.addItem(new Roo.menu.TextItem({ text : text }));
19783     },
19784
19785     /**
19786      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19787      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19788      * @param {Roo.menu.Item} item The menu item to add
19789      * @return {Roo.menu.Item} The menu item that was added
19790      */
19791     insert : function(index, item){
19792         this.items.insert(index, item);
19793         if(this.ul){
19794             var li = document.createElement("li");
19795             li.className = "x-menu-list-item";
19796             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19797             item.render(li, this);
19798             this.delayAutoWidth();
19799         }
19800         return item;
19801     },
19802
19803     /**
19804      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19805      * @param {Roo.menu.Item} item The menu item to remove
19806      */
19807     remove : function(item){
19808         this.items.removeKey(item.id);
19809         item.destroy();
19810     },
19811
19812     /**
19813      * Removes and destroys all items in the menu
19814      */
19815     removeAll : function(){
19816         var f;
19817         while(f = this.items.first()){
19818             this.remove(f);
19819         }
19820     }
19821 });
19822
19823 // MenuNav is a private utility class used internally by the Menu
19824 Roo.menu.MenuNav = function(menu){
19825     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19826     this.scope = this.menu = menu;
19827 };
19828
19829 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19830     doRelay : function(e, h){
19831         var k = e.getKey();
19832         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19833             this.menu.tryActivate(0, 1);
19834             return false;
19835         }
19836         return h.call(this.scope || this, e, this.menu);
19837     },
19838
19839     up : function(e, m){
19840         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19841             m.tryActivate(m.items.length-1, -1);
19842         }
19843     },
19844
19845     down : function(e, m){
19846         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19847             m.tryActivate(0, 1);
19848         }
19849     },
19850
19851     right : function(e, m){
19852         if(m.activeItem){
19853             m.activeItem.expandMenu(true);
19854         }
19855     },
19856
19857     left : function(e, m){
19858         m.hide();
19859         if(m.parentMenu && m.parentMenu.activeItem){
19860             m.parentMenu.activeItem.activate();
19861         }
19862     },
19863
19864     enter : function(e, m){
19865         if(m.activeItem){
19866             e.stopPropagation();
19867             m.activeItem.onClick(e);
19868             m.fireEvent("click", this, m.activeItem);
19869             return true;
19870         }
19871     }
19872 });/*
19873  * Based on:
19874  * Ext JS Library 1.1.1
19875  * Copyright(c) 2006-2007, Ext JS, LLC.
19876  *
19877  * Originally Released Under LGPL - original licence link has changed is not relivant.
19878  *
19879  * Fork - LGPL
19880  * <script type="text/javascript">
19881  */
19882  
19883 /**
19884  * @class Roo.menu.MenuMgr
19885  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19886  * @singleton
19887  */
19888 Roo.menu.MenuMgr = function(){
19889    var menus, active, groups = {}, attached = false, lastShow = new Date();
19890
19891    // private - called when first menu is created
19892    function init(){
19893        menus = {};
19894        active = new Roo.util.MixedCollection();
19895        Roo.get(document).addKeyListener(27, function(){
19896            if(active.length > 0){
19897                hideAll();
19898            }
19899        });
19900    }
19901
19902    // private
19903    function hideAll(){
19904        if(active && active.length > 0){
19905            var c = active.clone();
19906            c.each(function(m){
19907                m.hide();
19908            });
19909        }
19910    }
19911
19912    // private
19913    function onHide(m){
19914        active.remove(m);
19915        if(active.length < 1){
19916            Roo.get(document).un("mousedown", onMouseDown);
19917            attached = false;
19918        }
19919    }
19920
19921    // private
19922    function onShow(m){
19923        var last = active.last();
19924        lastShow = new Date();
19925        active.add(m);
19926        if(!attached){
19927            Roo.get(document).on("mousedown", onMouseDown);
19928            attached = true;
19929        }
19930        if(m.parentMenu){
19931           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19932           m.parentMenu.activeChild = m;
19933        }else if(last && last.isVisible()){
19934           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19935        }
19936    }
19937
19938    // private
19939    function onBeforeHide(m){
19940        if(m.activeChild){
19941            m.activeChild.hide();
19942        }
19943        if(m.autoHideTimer){
19944            clearTimeout(m.autoHideTimer);
19945            delete m.autoHideTimer;
19946        }
19947    }
19948
19949    // private
19950    function onBeforeShow(m){
19951        var pm = m.parentMenu;
19952        if(!pm && !m.allowOtherMenus){
19953            hideAll();
19954        }else if(pm && pm.activeChild && active != m){
19955            pm.activeChild.hide();
19956        }
19957    }
19958
19959    // private
19960    function onMouseDown(e){
19961        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19962            hideAll();
19963        }
19964    }
19965
19966    // private
19967    function onBeforeCheck(mi, state){
19968        if(state){
19969            var g = groups[mi.group];
19970            for(var i = 0, l = g.length; i < l; i++){
19971                if(g[i] != mi){
19972                    g[i].setChecked(false);
19973                }
19974            }
19975        }
19976    }
19977
19978    return {
19979
19980        /**
19981         * Hides all menus that are currently visible
19982         */
19983        hideAll : function(){
19984             hideAll();  
19985        },
19986
19987        // private
19988        register : function(menu){
19989            if(!menus){
19990                init();
19991            }
19992            menus[menu.id] = menu;
19993            menu.on("beforehide", onBeforeHide);
19994            menu.on("hide", onHide);
19995            menu.on("beforeshow", onBeforeShow);
19996            menu.on("show", onShow);
19997            var g = menu.group;
19998            if(g && menu.events["checkchange"]){
19999                if(!groups[g]){
20000                    groups[g] = [];
20001                }
20002                groups[g].push(menu);
20003                menu.on("checkchange", onCheck);
20004            }
20005        },
20006
20007         /**
20008          * Returns a {@link Roo.menu.Menu} object
20009          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20010          * be used to generate and return a new Menu instance.
20011          */
20012        get : function(menu){
20013            if(typeof menu == "string"){ // menu id
20014                return menus[menu];
20015            }else if(menu.events){  // menu instance
20016                return menu;
20017            }else if(typeof menu.length == 'number'){ // array of menu items?
20018                return new Roo.menu.Menu({items:menu});
20019            }else{ // otherwise, must be a config
20020                return new Roo.menu.Menu(menu);
20021            }
20022        },
20023
20024        // private
20025        unregister : function(menu){
20026            delete menus[menu.id];
20027            menu.un("beforehide", onBeforeHide);
20028            menu.un("hide", onHide);
20029            menu.un("beforeshow", onBeforeShow);
20030            menu.un("show", onShow);
20031            var g = menu.group;
20032            if(g && menu.events["checkchange"]){
20033                groups[g].remove(menu);
20034                menu.un("checkchange", onCheck);
20035            }
20036        },
20037
20038        // private
20039        registerCheckable : function(menuItem){
20040            var g = menuItem.group;
20041            if(g){
20042                if(!groups[g]){
20043                    groups[g] = [];
20044                }
20045                groups[g].push(menuItem);
20046                menuItem.on("beforecheckchange", onBeforeCheck);
20047            }
20048        },
20049
20050        // private
20051        unregisterCheckable : function(menuItem){
20052            var g = menuItem.group;
20053            if(g){
20054                groups[g].remove(menuItem);
20055                menuItem.un("beforecheckchange", onBeforeCheck);
20056            }
20057        }
20058    };
20059 }();/*
20060  * Based on:
20061  * Ext JS Library 1.1.1
20062  * Copyright(c) 2006-2007, Ext JS, LLC.
20063  *
20064  * Originally Released Under LGPL - original licence link has changed is not relivant.
20065  *
20066  * Fork - LGPL
20067  * <script type="text/javascript">
20068  */
20069  
20070
20071 /**
20072  * @class Roo.menu.BaseItem
20073  * @extends Roo.Component
20074  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20075  * management and base configuration options shared by all menu components.
20076  * @constructor
20077  * Creates a new BaseItem
20078  * @param {Object} config Configuration options
20079  */
20080 Roo.menu.BaseItem = function(config){
20081     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20082
20083     this.addEvents({
20084         /**
20085          * @event click
20086          * Fires when this item is clicked
20087          * @param {Roo.menu.BaseItem} this
20088          * @param {Roo.EventObject} e
20089          */
20090         click: true,
20091         /**
20092          * @event activate
20093          * Fires when this item is activated
20094          * @param {Roo.menu.BaseItem} this
20095          */
20096         activate : true,
20097         /**
20098          * @event deactivate
20099          * Fires when this item is deactivated
20100          * @param {Roo.menu.BaseItem} this
20101          */
20102         deactivate : true
20103     });
20104
20105     if(this.handler){
20106         this.on("click", this.handler, this.scope, true);
20107     }
20108 };
20109
20110 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20111     /**
20112      * @cfg {Function} handler
20113      * A function that will handle the click event of this menu item (defaults to undefined)
20114      */
20115     /**
20116      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20117      */
20118     canActivate : false,
20119     /**
20120      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20121      */
20122     activeClass : "x-menu-item-active",
20123     /**
20124      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20125      */
20126     hideOnClick : true,
20127     /**
20128      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20129      */
20130     hideDelay : 100,
20131
20132     // private
20133     ctype: "Roo.menu.BaseItem",
20134
20135     // private
20136     actionMode : "container",
20137
20138     // private
20139     render : function(container, parentMenu){
20140         this.parentMenu = parentMenu;
20141         Roo.menu.BaseItem.superclass.render.call(this, container);
20142         this.container.menuItemId = this.id;
20143     },
20144
20145     // private
20146     onRender : function(container, position){
20147         this.el = Roo.get(this.el);
20148         container.dom.appendChild(this.el.dom);
20149     },
20150
20151     // private
20152     onClick : function(e){
20153         if(!this.disabled && this.fireEvent("click", this, e) !== false
20154                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20155             this.handleClick(e);
20156         }else{
20157             e.stopEvent();
20158         }
20159     },
20160
20161     // private
20162     activate : function(){
20163         if(this.disabled){
20164             return false;
20165         }
20166         var li = this.container;
20167         li.addClass(this.activeClass);
20168         this.region = li.getRegion().adjust(2, 2, -2, -2);
20169         this.fireEvent("activate", this);
20170         return true;
20171     },
20172
20173     // private
20174     deactivate : function(){
20175         this.container.removeClass(this.activeClass);
20176         this.fireEvent("deactivate", this);
20177     },
20178
20179     // private
20180     shouldDeactivate : function(e){
20181         return !this.region || !this.region.contains(e.getPoint());
20182     },
20183
20184     // private
20185     handleClick : function(e){
20186         if(this.hideOnClick){
20187             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20188         }
20189     },
20190
20191     // private
20192     expandMenu : function(autoActivate){
20193         // do nothing
20194     },
20195
20196     // private
20197     hideMenu : function(){
20198         // do nothing
20199     }
20200 });/*
20201  * Based on:
20202  * Ext JS Library 1.1.1
20203  * Copyright(c) 2006-2007, Ext JS, LLC.
20204  *
20205  * Originally Released Under LGPL - original licence link has changed is not relivant.
20206  *
20207  * Fork - LGPL
20208  * <script type="text/javascript">
20209  */
20210  
20211 /**
20212  * @class Roo.menu.Adapter
20213  * @extends Roo.menu.BaseItem
20214  * 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.
20215  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20216  * @constructor
20217  * Creates a new Adapter
20218  * @param {Object} config Configuration options
20219  */
20220 Roo.menu.Adapter = function(component, config){
20221     Roo.menu.Adapter.superclass.constructor.call(this, config);
20222     this.component = component;
20223 };
20224 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20225     // private
20226     canActivate : true,
20227
20228     // private
20229     onRender : function(container, position){
20230         this.component.render(container);
20231         this.el = this.component.getEl();
20232     },
20233
20234     // private
20235     activate : function(){
20236         if(this.disabled){
20237             return false;
20238         }
20239         this.component.focus();
20240         this.fireEvent("activate", this);
20241         return true;
20242     },
20243
20244     // private
20245     deactivate : function(){
20246         this.fireEvent("deactivate", this);
20247     },
20248
20249     // private
20250     disable : function(){
20251         this.component.disable();
20252         Roo.menu.Adapter.superclass.disable.call(this);
20253     },
20254
20255     // private
20256     enable : function(){
20257         this.component.enable();
20258         Roo.menu.Adapter.superclass.enable.call(this);
20259     }
20260 });/*
20261  * Based on:
20262  * Ext JS Library 1.1.1
20263  * Copyright(c) 2006-2007, Ext JS, LLC.
20264  *
20265  * Originally Released Under LGPL - original licence link has changed is not relivant.
20266  *
20267  * Fork - LGPL
20268  * <script type="text/javascript">
20269  */
20270
20271 /**
20272  * @class Roo.menu.TextItem
20273  * @extends Roo.menu.BaseItem
20274  * Adds a static text string to a menu, usually used as either a heading or group separator.
20275  * Note: old style constructor with text is still supported.
20276  * 
20277  * @constructor
20278  * Creates a new TextItem
20279  * @param {Object} cfg Configuration
20280  */
20281 Roo.menu.TextItem = function(cfg){
20282     if (typeof(cfg) == 'string') {
20283         this.text = cfg;
20284     } else {
20285         Roo.apply(this,cfg);
20286     }
20287     
20288     Roo.menu.TextItem.superclass.constructor.call(this);
20289 };
20290
20291 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20292     /**
20293      * @cfg {Boolean} text Text to show on item.
20294      */
20295     text : '',
20296     
20297     /**
20298      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20299      */
20300     hideOnClick : false,
20301     /**
20302      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20303      */
20304     itemCls : "x-menu-text",
20305
20306     // private
20307     onRender : function(){
20308         var s = document.createElement("span");
20309         s.className = this.itemCls;
20310         s.innerHTML = this.text;
20311         this.el = s;
20312         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20313     }
20314 });/*
20315  * Based on:
20316  * Ext JS Library 1.1.1
20317  * Copyright(c) 2006-2007, Ext JS, LLC.
20318  *
20319  * Originally Released Under LGPL - original licence link has changed is not relivant.
20320  *
20321  * Fork - LGPL
20322  * <script type="text/javascript">
20323  */
20324
20325 /**
20326  * @class Roo.menu.Separator
20327  * @extends Roo.menu.BaseItem
20328  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20329  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20330  * @constructor
20331  * @param {Object} config Configuration options
20332  */
20333 Roo.menu.Separator = function(config){
20334     Roo.menu.Separator.superclass.constructor.call(this, config);
20335 };
20336
20337 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20338     /**
20339      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20340      */
20341     itemCls : "x-menu-sep",
20342     /**
20343      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20344      */
20345     hideOnClick : false,
20346
20347     // private
20348     onRender : function(li){
20349         var s = document.createElement("span");
20350         s.className = this.itemCls;
20351         s.innerHTML = "&#160;";
20352         this.el = s;
20353         li.addClass("x-menu-sep-li");
20354         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20355     }
20356 });/*
20357  * Based on:
20358  * Ext JS Library 1.1.1
20359  * Copyright(c) 2006-2007, Ext JS, LLC.
20360  *
20361  * Originally Released Under LGPL - original licence link has changed is not relivant.
20362  *
20363  * Fork - LGPL
20364  * <script type="text/javascript">
20365  */
20366 /**
20367  * @class Roo.menu.Item
20368  * @extends Roo.menu.BaseItem
20369  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20370  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20371  * activation and click handling.
20372  * @constructor
20373  * Creates a new Item
20374  * @param {Object} config Configuration options
20375  */
20376 Roo.menu.Item = function(config){
20377     Roo.menu.Item.superclass.constructor.call(this, config);
20378     if(this.menu){
20379         this.menu = Roo.menu.MenuMgr.get(this.menu);
20380     }
20381 };
20382 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20383     
20384     /**
20385      * @cfg {String} text
20386      * The text to show on the menu item.
20387      */
20388     text: '',
20389      /**
20390      * @cfg {String} HTML to render in menu
20391      * The text to show on the menu item (HTML version).
20392      */
20393     html: '',
20394     /**
20395      * @cfg {String} icon
20396      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20397      */
20398     icon: undefined,
20399     /**
20400      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20401      */
20402     itemCls : "x-menu-item",
20403     /**
20404      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20405      */
20406     canActivate : true,
20407     /**
20408      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20409      */
20410     showDelay: 200,
20411     // doc'd in BaseItem
20412     hideDelay: 200,
20413
20414     // private
20415     ctype: "Roo.menu.Item",
20416     
20417     // private
20418     onRender : function(container, position){
20419         var el = document.createElement("a");
20420         el.hideFocus = true;
20421         el.unselectable = "on";
20422         el.href = this.href || "#";
20423         if(this.hrefTarget){
20424             el.target = this.hrefTarget;
20425         }
20426         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20427         
20428         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20429         
20430         el.innerHTML = String.format(
20431                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20432                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20433         this.el = el;
20434         Roo.menu.Item.superclass.onRender.call(this, container, position);
20435     },
20436
20437     /**
20438      * Sets the text to display in this menu item
20439      * @param {String} text The text to display
20440      * @param {Boolean} isHTML true to indicate text is pure html.
20441      */
20442     setText : function(text, isHTML){
20443         if (isHTML) {
20444             this.html = text;
20445         } else {
20446             this.text = text;
20447             this.html = '';
20448         }
20449         if(this.rendered){
20450             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20451      
20452             this.el.update(String.format(
20453                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20454                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20455             this.parentMenu.autoWidth();
20456         }
20457     },
20458
20459     // private
20460     handleClick : function(e){
20461         if(!this.href){ // if no link defined, stop the event automatically
20462             e.stopEvent();
20463         }
20464         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20465     },
20466
20467     // private
20468     activate : function(autoExpand){
20469         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20470             this.focus();
20471             if(autoExpand){
20472                 this.expandMenu();
20473             }
20474         }
20475         return true;
20476     },
20477
20478     // private
20479     shouldDeactivate : function(e){
20480         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20481             if(this.menu && this.menu.isVisible()){
20482                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20483             }
20484             return true;
20485         }
20486         return false;
20487     },
20488
20489     // private
20490     deactivate : function(){
20491         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20492         this.hideMenu();
20493     },
20494
20495     // private
20496     expandMenu : function(autoActivate){
20497         if(!this.disabled && this.menu){
20498             clearTimeout(this.hideTimer);
20499             delete this.hideTimer;
20500             if(!this.menu.isVisible() && !this.showTimer){
20501                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20502             }else if (this.menu.isVisible() && autoActivate){
20503                 this.menu.tryActivate(0, 1);
20504             }
20505         }
20506     },
20507
20508     // private
20509     deferExpand : function(autoActivate){
20510         delete this.showTimer;
20511         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20512         if(autoActivate){
20513             this.menu.tryActivate(0, 1);
20514         }
20515     },
20516
20517     // private
20518     hideMenu : function(){
20519         clearTimeout(this.showTimer);
20520         delete this.showTimer;
20521         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20522             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20523         }
20524     },
20525
20526     // private
20527     deferHide : function(){
20528         delete this.hideTimer;
20529         this.menu.hide();
20530     }
20531 });/*
20532  * Based on:
20533  * Ext JS Library 1.1.1
20534  * Copyright(c) 2006-2007, Ext JS, LLC.
20535  *
20536  * Originally Released Under LGPL - original licence link has changed is not relivant.
20537  *
20538  * Fork - LGPL
20539  * <script type="text/javascript">
20540  */
20541  
20542 /**
20543  * @class Roo.menu.CheckItem
20544  * @extends Roo.menu.Item
20545  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20546  * @constructor
20547  * Creates a new CheckItem
20548  * @param {Object} config Configuration options
20549  */
20550 Roo.menu.CheckItem = function(config){
20551     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20552     this.addEvents({
20553         /**
20554          * @event beforecheckchange
20555          * Fires before the checked value is set, providing an opportunity to cancel if needed
20556          * @param {Roo.menu.CheckItem} this
20557          * @param {Boolean} checked The new checked value that will be set
20558          */
20559         "beforecheckchange" : true,
20560         /**
20561          * @event checkchange
20562          * Fires after the checked value has been set
20563          * @param {Roo.menu.CheckItem} this
20564          * @param {Boolean} checked The checked value that was set
20565          */
20566         "checkchange" : true
20567     });
20568     if(this.checkHandler){
20569         this.on('checkchange', this.checkHandler, this.scope);
20570     }
20571 };
20572 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20573     /**
20574      * @cfg {String} group
20575      * All check items with the same group name will automatically be grouped into a single-select
20576      * radio button group (defaults to '')
20577      */
20578     /**
20579      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20580      */
20581     itemCls : "x-menu-item x-menu-check-item",
20582     /**
20583      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20584      */
20585     groupClass : "x-menu-group-item",
20586
20587     /**
20588      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20589      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20590      * initialized with checked = true will be rendered as checked.
20591      */
20592     checked: false,
20593
20594     // private
20595     ctype: "Roo.menu.CheckItem",
20596
20597     // private
20598     onRender : function(c){
20599         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20600         if(this.group){
20601             this.el.addClass(this.groupClass);
20602         }
20603         Roo.menu.MenuMgr.registerCheckable(this);
20604         if(this.checked){
20605             this.checked = false;
20606             this.setChecked(true, true);
20607         }
20608     },
20609
20610     // private
20611     destroy : function(){
20612         if(this.rendered){
20613             Roo.menu.MenuMgr.unregisterCheckable(this);
20614         }
20615         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20616     },
20617
20618     /**
20619      * Set the checked state of this item
20620      * @param {Boolean} checked The new checked value
20621      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20622      */
20623     setChecked : function(state, suppressEvent){
20624         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20625             if(this.container){
20626                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20627             }
20628             this.checked = state;
20629             if(suppressEvent !== true){
20630                 this.fireEvent("checkchange", this, state);
20631             }
20632         }
20633     },
20634
20635     // private
20636     handleClick : function(e){
20637        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20638            this.setChecked(!this.checked);
20639        }
20640        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20641     }
20642 });/*
20643  * Based on:
20644  * Ext JS Library 1.1.1
20645  * Copyright(c) 2006-2007, Ext JS, LLC.
20646  *
20647  * Originally Released Under LGPL - original licence link has changed is not relivant.
20648  *
20649  * Fork - LGPL
20650  * <script type="text/javascript">
20651  */
20652  
20653 /**
20654  * @class Roo.menu.DateItem
20655  * @extends Roo.menu.Adapter
20656  * A menu item that wraps the {@link Roo.DatPicker} component.
20657  * @constructor
20658  * Creates a new DateItem
20659  * @param {Object} config Configuration options
20660  */
20661 Roo.menu.DateItem = function(config){
20662     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20663     /** The Roo.DatePicker object @type Roo.DatePicker */
20664     this.picker = this.component;
20665     this.addEvents({select: true});
20666     
20667     this.picker.on("render", function(picker){
20668         picker.getEl().swallowEvent("click");
20669         picker.container.addClass("x-menu-date-item");
20670     });
20671
20672     this.picker.on("select", this.onSelect, this);
20673 };
20674
20675 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20676     // private
20677     onSelect : function(picker, date){
20678         this.fireEvent("select", this, date, picker);
20679         Roo.menu.DateItem.superclass.handleClick.call(this);
20680     }
20681 });/*
20682  * Based on:
20683  * Ext JS Library 1.1.1
20684  * Copyright(c) 2006-2007, Ext JS, LLC.
20685  *
20686  * Originally Released Under LGPL - original licence link has changed is not relivant.
20687  *
20688  * Fork - LGPL
20689  * <script type="text/javascript">
20690  */
20691  
20692 /**
20693  * @class Roo.menu.ColorItem
20694  * @extends Roo.menu.Adapter
20695  * A menu item that wraps the {@link Roo.ColorPalette} component.
20696  * @constructor
20697  * Creates a new ColorItem
20698  * @param {Object} config Configuration options
20699  */
20700 Roo.menu.ColorItem = function(config){
20701     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20702     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20703     this.palette = this.component;
20704     this.relayEvents(this.palette, ["select"]);
20705     if(this.selectHandler){
20706         this.on('select', this.selectHandler, this.scope);
20707     }
20708 };
20709 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20710  * Based on:
20711  * Ext JS Library 1.1.1
20712  * Copyright(c) 2006-2007, Ext JS, LLC.
20713  *
20714  * Originally Released Under LGPL - original licence link has changed is not relivant.
20715  *
20716  * Fork - LGPL
20717  * <script type="text/javascript">
20718  */
20719  
20720
20721 /**
20722  * @class Roo.menu.DateMenu
20723  * @extends Roo.menu.Menu
20724  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20725  * @constructor
20726  * Creates a new DateMenu
20727  * @param {Object} config Configuration options
20728  */
20729 Roo.menu.DateMenu = function(config){
20730     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20731     this.plain = true;
20732     var di = new Roo.menu.DateItem(config);
20733     this.add(di);
20734     /**
20735      * The {@link Roo.DatePicker} instance for this DateMenu
20736      * @type DatePicker
20737      */
20738     this.picker = di.picker;
20739     /**
20740      * @event select
20741      * @param {DatePicker} picker
20742      * @param {Date} date
20743      */
20744     this.relayEvents(di, ["select"]);
20745
20746     this.on('beforeshow', function(){
20747         if(this.picker){
20748             this.picker.hideMonthPicker(true);
20749         }
20750     }, this);
20751 };
20752 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20753     cls:'x-date-menu'
20754 });/*
20755  * Based on:
20756  * Ext JS Library 1.1.1
20757  * Copyright(c) 2006-2007, Ext JS, LLC.
20758  *
20759  * Originally Released Under LGPL - original licence link has changed is not relivant.
20760  *
20761  * Fork - LGPL
20762  * <script type="text/javascript">
20763  */
20764  
20765
20766 /**
20767  * @class Roo.menu.ColorMenu
20768  * @extends Roo.menu.Menu
20769  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20770  * @constructor
20771  * Creates a new ColorMenu
20772  * @param {Object} config Configuration options
20773  */
20774 Roo.menu.ColorMenu = function(config){
20775     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20776     this.plain = true;
20777     var ci = new Roo.menu.ColorItem(config);
20778     this.add(ci);
20779     /**
20780      * The {@link Roo.ColorPalette} instance for this ColorMenu
20781      * @type ColorPalette
20782      */
20783     this.palette = ci.palette;
20784     /**
20785      * @event select
20786      * @param {ColorPalette} palette
20787      * @param {String} color
20788      */
20789     this.relayEvents(ci, ["select"]);
20790 };
20791 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20792  * Based on:
20793  * Ext JS Library 1.1.1
20794  * Copyright(c) 2006-2007, Ext JS, LLC.
20795  *
20796  * Originally Released Under LGPL - original licence link has changed is not relivant.
20797  *
20798  * Fork - LGPL
20799  * <script type="text/javascript">
20800  */
20801  
20802 /**
20803  * @class Roo.form.Field
20804  * @extends Roo.BoxComponent
20805  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20806  * @constructor
20807  * Creates a new Field
20808  * @param {Object} config Configuration options
20809  */
20810 Roo.form.Field = function(config){
20811     Roo.form.Field.superclass.constructor.call(this, config);
20812 };
20813
20814 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20815     /**
20816      * @cfg {String} fieldLabel Label to use when rendering a form.
20817      */
20818        /**
20819      * @cfg {String} qtip Mouse over tip
20820      */
20821      
20822     /**
20823      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20824      */
20825     invalidClass : "x-form-invalid",
20826     /**
20827      * @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")
20828      */
20829     invalidText : "The value in this field is invalid",
20830     /**
20831      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20832      */
20833     focusClass : "x-form-focus",
20834     /**
20835      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20836       automatic validation (defaults to "keyup").
20837      */
20838     validationEvent : "keyup",
20839     /**
20840      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20841      */
20842     validateOnBlur : true,
20843     /**
20844      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20845      */
20846     validationDelay : 250,
20847     /**
20848      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20849      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20850      */
20851     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20852     /**
20853      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20854      */
20855     fieldClass : "x-form-field",
20856     /**
20857      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20858      *<pre>
20859 Value         Description
20860 -----------   ----------------------------------------------------------------------
20861 qtip          Display a quick tip when the user hovers over the field
20862 title         Display a default browser title attribute popup
20863 under         Add a block div beneath the field containing the error text
20864 side          Add an error icon to the right of the field with a popup on hover
20865 [element id]  Add the error text directly to the innerHTML of the specified element
20866 </pre>
20867      */
20868     msgTarget : 'qtip',
20869     /**
20870      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20871      */
20872     msgFx : 'normal',
20873
20874     /**
20875      * @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.
20876      */
20877     readOnly : false,
20878
20879     /**
20880      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20881      */
20882     disabled : false,
20883
20884     /**
20885      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20886      */
20887     inputType : undefined,
20888     
20889     /**
20890      * @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).
20891          */
20892         tabIndex : undefined,
20893         
20894     // private
20895     isFormField : true,
20896
20897     // private
20898     hasFocus : false,
20899     /**
20900      * @property {Roo.Element} fieldEl
20901      * Element Containing the rendered Field (with label etc.)
20902      */
20903     /**
20904      * @cfg {Mixed} value A value to initialize this field with.
20905      */
20906     value : undefined,
20907
20908     /**
20909      * @cfg {String} name The field's HTML name attribute.
20910      */
20911     /**
20912      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20913      */
20914
20915         // private ??
20916         initComponent : function(){
20917         Roo.form.Field.superclass.initComponent.call(this);
20918         this.addEvents({
20919             /**
20920              * @event focus
20921              * Fires when this field receives input focus.
20922              * @param {Roo.form.Field} this
20923              */
20924             focus : true,
20925             /**
20926              * @event blur
20927              * Fires when this field loses input focus.
20928              * @param {Roo.form.Field} this
20929              */
20930             blur : true,
20931             /**
20932              * @event specialkey
20933              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20934              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20935              * @param {Roo.form.Field} this
20936              * @param {Roo.EventObject} e The event object
20937              */
20938             specialkey : true,
20939             /**
20940              * @event change
20941              * Fires just before the field blurs if the field value has changed.
20942              * @param {Roo.form.Field} this
20943              * @param {Mixed} newValue The new value
20944              * @param {Mixed} oldValue The original value
20945              */
20946             change : true,
20947             /**
20948              * @event invalid
20949              * Fires after the field has been marked as invalid.
20950              * @param {Roo.form.Field} this
20951              * @param {String} msg The validation message
20952              */
20953             invalid : true,
20954             /**
20955              * @event valid
20956              * Fires after the field has been validated with no errors.
20957              * @param {Roo.form.Field} this
20958              */
20959             valid : true,
20960              /**
20961              * @event keyup
20962              * Fires after the key up
20963              * @param {Roo.form.Field} this
20964              * @param {Roo.EventObject}  e The event Object
20965              */
20966             keyup : true
20967         });
20968     },
20969
20970     /**
20971      * Returns the name attribute of the field if available
20972      * @return {String} name The field name
20973      */
20974     getName: function(){
20975          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20976     },
20977
20978     // private
20979     onRender : function(ct, position){
20980         Roo.form.Field.superclass.onRender.call(this, ct, position);
20981         if(!this.el){
20982             var cfg = this.getAutoCreate();
20983             if(!cfg.name){
20984                 cfg.name = this.name || this.id;
20985             }
20986             if(this.inputType){
20987                 cfg.type = this.inputType;
20988             }
20989             this.el = ct.createChild(cfg, position);
20990         }
20991         var type = this.el.dom.type;
20992         if(type){
20993             if(type == 'password'){
20994                 type = 'text';
20995             }
20996             this.el.addClass('x-form-'+type);
20997         }
20998         if(this.readOnly){
20999             this.el.dom.readOnly = true;
21000         }
21001         if(this.tabIndex !== undefined){
21002             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21003         }
21004
21005         this.el.addClass([this.fieldClass, this.cls]);
21006         this.initValue();
21007     },
21008
21009     /**
21010      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21011      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21012      * @return {Roo.form.Field} this
21013      */
21014     applyTo : function(target){
21015         this.allowDomMove = false;
21016         this.el = Roo.get(target);
21017         this.render(this.el.dom.parentNode);
21018         return this;
21019     },
21020
21021     // private
21022     initValue : function(){
21023         if(this.value !== undefined){
21024             this.setValue(this.value);
21025         }else if(this.el.dom.value.length > 0){
21026             this.setValue(this.el.dom.value);
21027         }
21028     },
21029
21030     /**
21031      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21032      */
21033     isDirty : function() {
21034         if(this.disabled) {
21035             return false;
21036         }
21037         return String(this.getValue()) !== String(this.originalValue);
21038     },
21039
21040     // private
21041     afterRender : function(){
21042         Roo.form.Field.superclass.afterRender.call(this);
21043         this.initEvents();
21044     },
21045
21046     // private
21047     fireKey : function(e){
21048         //Roo.log('field ' + e.getKey());
21049         if(e.isNavKeyPress()){
21050             this.fireEvent("specialkey", this, e);
21051         }
21052     },
21053
21054     /**
21055      * Resets the current field value to the originally loaded value and clears any validation messages
21056      */
21057     reset : function(){
21058         this.setValue(this.originalValue);
21059         this.clearInvalid();
21060     },
21061
21062     // private
21063     initEvents : function(){
21064         // safari killled keypress - so keydown is now used..
21065         this.el.on("keydown" , this.fireKey,  this);
21066         this.el.on("focus", this.onFocus,  this);
21067         this.el.on("blur", this.onBlur,  this);
21068         this.el.relayEvent('keyup', this);
21069
21070         // reference to original value for reset
21071         this.originalValue = this.getValue();
21072     },
21073
21074     // private
21075     onFocus : function(){
21076         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21077             this.el.addClass(this.focusClass);
21078         }
21079         if(!this.hasFocus){
21080             this.hasFocus = true;
21081             this.startValue = this.getValue();
21082             this.fireEvent("focus", this);
21083         }
21084     },
21085
21086     beforeBlur : Roo.emptyFn,
21087
21088     // private
21089     onBlur : function(){
21090         this.beforeBlur();
21091         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21092             this.el.removeClass(this.focusClass);
21093         }
21094         this.hasFocus = false;
21095         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21096             this.validate();
21097         }
21098         var v = this.getValue();
21099         if(String(v) !== String(this.startValue)){
21100             this.fireEvent('change', this, v, this.startValue);
21101         }
21102         this.fireEvent("blur", this);
21103     },
21104
21105     /**
21106      * Returns whether or not the field value is currently valid
21107      * @param {Boolean} preventMark True to disable marking the field invalid
21108      * @return {Boolean} True if the value is valid, else false
21109      */
21110     isValid : function(preventMark){
21111         if(this.disabled){
21112             return true;
21113         }
21114         var restore = this.preventMark;
21115         this.preventMark = preventMark === true;
21116         var v = this.validateValue(this.processValue(this.getRawValue()));
21117         this.preventMark = restore;
21118         return v;
21119     },
21120
21121     /**
21122      * Validates the field value
21123      * @return {Boolean} True if the value is valid, else false
21124      */
21125     validate : function(){
21126         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21127             this.clearInvalid();
21128             return true;
21129         }
21130         return false;
21131     },
21132
21133     processValue : function(value){
21134         return value;
21135     },
21136
21137     // private
21138     // Subclasses should provide the validation implementation by overriding this
21139     validateValue : function(value){
21140         return true;
21141     },
21142
21143     /**
21144      * Mark this field as invalid
21145      * @param {String} msg The validation message
21146      */
21147     markInvalid : function(msg){
21148         if(!this.rendered || this.preventMark){ // not rendered
21149             return;
21150         }
21151         this.el.addClass(this.invalidClass);
21152         msg = msg || this.invalidText;
21153         switch(this.msgTarget){
21154             case 'qtip':
21155                 this.el.dom.qtip = msg;
21156                 this.el.dom.qclass = 'x-form-invalid-tip';
21157                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21158                     Roo.QuickTips.enable();
21159                 }
21160                 break;
21161             case 'title':
21162                 this.el.dom.title = msg;
21163                 break;
21164             case 'under':
21165                 if(!this.errorEl){
21166                     var elp = this.el.findParent('.x-form-element', 5, true);
21167                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21168                     this.errorEl.setWidth(elp.getWidth(true)-20);
21169                 }
21170                 this.errorEl.update(msg);
21171                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21172                 break;
21173             case 'side':
21174                 if(!this.errorIcon){
21175                     var elp = this.el.findParent('.x-form-element', 5, true);
21176                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21177                 }
21178                 this.alignErrorIcon();
21179                 this.errorIcon.dom.qtip = msg;
21180                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21181                 this.errorIcon.show();
21182                 this.on('resize', this.alignErrorIcon, this);
21183                 break;
21184             default:
21185                 var t = Roo.getDom(this.msgTarget);
21186                 t.innerHTML = msg;
21187                 t.style.display = this.msgDisplay;
21188                 break;
21189         }
21190         this.fireEvent('invalid', this, msg);
21191     },
21192
21193     // private
21194     alignErrorIcon : function(){
21195         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21196     },
21197
21198     /**
21199      * Clear any invalid styles/messages for this field
21200      */
21201     clearInvalid : function(){
21202         if(!this.rendered || this.preventMark){ // not rendered
21203             return;
21204         }
21205         this.el.removeClass(this.invalidClass);
21206         switch(this.msgTarget){
21207             case 'qtip':
21208                 this.el.dom.qtip = '';
21209                 break;
21210             case 'title':
21211                 this.el.dom.title = '';
21212                 break;
21213             case 'under':
21214                 if(this.errorEl){
21215                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21216                 }
21217                 break;
21218             case 'side':
21219                 if(this.errorIcon){
21220                     this.errorIcon.dom.qtip = '';
21221                     this.errorIcon.hide();
21222                     this.un('resize', this.alignErrorIcon, this);
21223                 }
21224                 break;
21225             default:
21226                 var t = Roo.getDom(this.msgTarget);
21227                 t.innerHTML = '';
21228                 t.style.display = 'none';
21229                 break;
21230         }
21231         this.fireEvent('valid', this);
21232     },
21233
21234     /**
21235      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21236      * @return {Mixed} value The field value
21237      */
21238     getRawValue : function(){
21239         var v = this.el.getValue();
21240         if(v === this.emptyText){
21241             v = '';
21242         }
21243         return v;
21244     },
21245
21246     /**
21247      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21248      * @return {Mixed} value The field value
21249      */
21250     getValue : function(){
21251         var v = this.el.getValue();
21252         if(v === this.emptyText || v === undefined){
21253             v = '';
21254         }
21255         return v;
21256     },
21257
21258     /**
21259      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21260      * @param {Mixed} value The value to set
21261      */
21262     setRawValue : function(v){
21263         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21264     },
21265
21266     /**
21267      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21268      * @param {Mixed} value The value to set
21269      */
21270     setValue : function(v){
21271         this.value = v;
21272         if(this.rendered){
21273             this.el.dom.value = (v === null || v === undefined ? '' : v);
21274             this.validate();
21275         }
21276     },
21277
21278     adjustSize : function(w, h){
21279         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21280         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21281         return s;
21282     },
21283
21284     adjustWidth : function(tag, w){
21285         tag = tag.toLowerCase();
21286         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21287             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21288                 if(tag == 'input'){
21289                     return w + 2;
21290                 }
21291                 if(tag = 'textarea'){
21292                     return w-2;
21293                 }
21294             }else if(Roo.isOpera){
21295                 if(tag == 'input'){
21296                     return w + 2;
21297                 }
21298                 if(tag = 'textarea'){
21299                     return w-2;
21300                 }
21301             }
21302         }
21303         return w;
21304     }
21305 });
21306
21307
21308 // anything other than normal should be considered experimental
21309 Roo.form.Field.msgFx = {
21310     normal : {
21311         show: function(msgEl, f){
21312             msgEl.setDisplayed('block');
21313         },
21314
21315         hide : function(msgEl, f){
21316             msgEl.setDisplayed(false).update('');
21317         }
21318     },
21319
21320     slide : {
21321         show: function(msgEl, f){
21322             msgEl.slideIn('t', {stopFx:true});
21323         },
21324
21325         hide : function(msgEl, f){
21326             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21327         }
21328     },
21329
21330     slideRight : {
21331         show: function(msgEl, f){
21332             msgEl.fixDisplay();
21333             msgEl.alignTo(f.el, 'tl-tr');
21334             msgEl.slideIn('l', {stopFx:true});
21335         },
21336
21337         hide : function(msgEl, f){
21338             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21339         }
21340     }
21341 };/*
21342  * Based on:
21343  * Ext JS Library 1.1.1
21344  * Copyright(c) 2006-2007, Ext JS, LLC.
21345  *
21346  * Originally Released Under LGPL - original licence link has changed is not relivant.
21347  *
21348  * Fork - LGPL
21349  * <script type="text/javascript">
21350  */
21351  
21352
21353 /**
21354  * @class Roo.form.TextField
21355  * @extends Roo.form.Field
21356  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21357  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21358  * @constructor
21359  * Creates a new TextField
21360  * @param {Object} config Configuration options
21361  */
21362 Roo.form.TextField = function(config){
21363     Roo.form.TextField.superclass.constructor.call(this, config);
21364     this.addEvents({
21365         /**
21366          * @event autosize
21367          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21368          * according to the default logic, but this event provides a hook for the developer to apply additional
21369          * logic at runtime to resize the field if needed.
21370              * @param {Roo.form.Field} this This text field
21371              * @param {Number} width The new field width
21372              */
21373         autosize : true
21374     });
21375 };
21376
21377 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21378     /**
21379      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21380      */
21381     grow : false,
21382     /**
21383      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21384      */
21385     growMin : 30,
21386     /**
21387      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21388      */
21389     growMax : 800,
21390     /**
21391      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21392      */
21393     vtype : null,
21394     /**
21395      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21396      */
21397     maskRe : null,
21398     /**
21399      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21400      */
21401     disableKeyFilter : false,
21402     /**
21403      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21404      */
21405     allowBlank : true,
21406     /**
21407      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21408      */
21409     minLength : 0,
21410     /**
21411      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21412      */
21413     maxLength : Number.MAX_VALUE,
21414     /**
21415      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21416      */
21417     minLengthText : "The minimum length for this field is {0}",
21418     /**
21419      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21420      */
21421     maxLengthText : "The maximum length for this field is {0}",
21422     /**
21423      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21424      */
21425     selectOnFocus : false,
21426     /**
21427      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21428      */
21429     blankText : "This field is required",
21430     /**
21431      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21432      * If available, this function will be called only after the basic validators all return true, and will be passed the
21433      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21434      */
21435     validator : null,
21436     /**
21437      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21438      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21439      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21440      */
21441     regex : null,
21442     /**
21443      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21444      */
21445     regexText : "",
21446     /**
21447      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21448      */
21449     emptyText : null,
21450     /**
21451      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21452      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21453      */
21454     emptyClass : 'x-form-empty-field',
21455
21456     // private
21457     initEvents : function(){
21458         Roo.form.TextField.superclass.initEvents.call(this);
21459         if(this.validationEvent == 'keyup'){
21460             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21461             this.el.on('keyup', this.filterValidation, this);
21462         }
21463         else if(this.validationEvent !== false){
21464             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21465         }
21466         if(this.selectOnFocus || this.emptyText){
21467             this.on("focus", this.preFocus, this);
21468             if(this.emptyText){
21469                 this.on('blur', this.postBlur, this);
21470                 this.applyEmptyText();
21471             }
21472         }
21473         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21474             this.el.on("keypress", this.filterKeys, this);
21475         }
21476         if(this.grow){
21477             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21478             this.el.on("click", this.autoSize,  this);
21479         }
21480     },
21481
21482     processValue : function(value){
21483         if(this.stripCharsRe){
21484             var newValue = value.replace(this.stripCharsRe, '');
21485             if(newValue !== value){
21486                 this.setRawValue(newValue);
21487                 return newValue;
21488             }
21489         }
21490         return value;
21491     },
21492
21493     filterValidation : function(e){
21494         if(!e.isNavKeyPress()){
21495             this.validationTask.delay(this.validationDelay);
21496         }
21497     },
21498
21499     // private
21500     onKeyUp : function(e){
21501         if(!e.isNavKeyPress()){
21502             this.autoSize();
21503         }
21504     },
21505
21506     /**
21507      * Resets the current field value to the originally-loaded value and clears any validation messages.
21508      * Also adds emptyText and emptyClass if the original value was blank.
21509      */
21510     reset : function(){
21511         Roo.form.TextField.superclass.reset.call(this);
21512         this.applyEmptyText();
21513     },
21514
21515     applyEmptyText : function(){
21516         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21517             this.setRawValue(this.emptyText);
21518             this.el.addClass(this.emptyClass);
21519         }
21520     },
21521
21522     // private
21523     preFocus : function(){
21524         if(this.emptyText){
21525             if(this.el.dom.value == this.emptyText){
21526                 this.setRawValue('');
21527             }
21528             this.el.removeClass(this.emptyClass);
21529         }
21530         if(this.selectOnFocus){
21531             this.el.dom.select();
21532         }
21533     },
21534
21535     // private
21536     postBlur : function(){
21537         this.applyEmptyText();
21538     },
21539
21540     // private
21541     filterKeys : function(e){
21542         var k = e.getKey();
21543         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21544             return;
21545         }
21546         var c = e.getCharCode(), cc = String.fromCharCode(c);
21547         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21548             return;
21549         }
21550         if(!this.maskRe.test(cc)){
21551             e.stopEvent();
21552         }
21553     },
21554
21555     setValue : function(v){
21556         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21557             this.el.removeClass(this.emptyClass);
21558         }
21559         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21560         this.applyEmptyText();
21561         this.autoSize();
21562     },
21563
21564     /**
21565      * Validates a value according to the field's validation rules and marks the field as invalid
21566      * if the validation fails
21567      * @param {Mixed} value The value to validate
21568      * @return {Boolean} True if the value is valid, else false
21569      */
21570     validateValue : function(value){
21571         if(value.length < 1 || value === this.emptyText){ // if it's blank
21572              if(this.allowBlank){
21573                 this.clearInvalid();
21574                 return true;
21575              }else{
21576                 this.markInvalid(this.blankText);
21577                 return false;
21578              }
21579         }
21580         if(value.length < this.minLength){
21581             this.markInvalid(String.format(this.minLengthText, this.minLength));
21582             return false;
21583         }
21584         if(value.length > this.maxLength){
21585             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21586             return false;
21587         }
21588         if(this.vtype){
21589             var vt = Roo.form.VTypes;
21590             if(!vt[this.vtype](value, this)){
21591                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21592                 return false;
21593             }
21594         }
21595         if(typeof this.validator == "function"){
21596             var msg = this.validator(value);
21597             if(msg !== true){
21598                 this.markInvalid(msg);
21599                 return false;
21600             }
21601         }
21602         if(this.regex && !this.regex.test(value)){
21603             this.markInvalid(this.regexText);
21604             return false;
21605         }
21606         return true;
21607     },
21608
21609     /**
21610      * Selects text in this field
21611      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21612      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21613      */
21614     selectText : function(start, end){
21615         var v = this.getRawValue();
21616         if(v.length > 0){
21617             start = start === undefined ? 0 : start;
21618             end = end === undefined ? v.length : end;
21619             var d = this.el.dom;
21620             if(d.setSelectionRange){
21621                 d.setSelectionRange(start, end);
21622             }else if(d.createTextRange){
21623                 var range = d.createTextRange();
21624                 range.moveStart("character", start);
21625                 range.moveEnd("character", v.length-end);
21626                 range.select();
21627             }
21628         }
21629     },
21630
21631     /**
21632      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21633      * This only takes effect if grow = true, and fires the autosize event.
21634      */
21635     autoSize : function(){
21636         if(!this.grow || !this.rendered){
21637             return;
21638         }
21639         if(!this.metrics){
21640             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21641         }
21642         var el = this.el;
21643         var v = el.dom.value;
21644         var d = document.createElement('div');
21645         d.appendChild(document.createTextNode(v));
21646         v = d.innerHTML;
21647         d = null;
21648         v += "&#160;";
21649         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21650         this.el.setWidth(w);
21651         this.fireEvent("autosize", this, w);
21652     }
21653 });/*
21654  * Based on:
21655  * Ext JS Library 1.1.1
21656  * Copyright(c) 2006-2007, Ext JS, LLC.
21657  *
21658  * Originally Released Under LGPL - original licence link has changed is not relivant.
21659  *
21660  * Fork - LGPL
21661  * <script type="text/javascript">
21662  */
21663  
21664 /**
21665  * @class Roo.form.Hidden
21666  * @extends Roo.form.TextField
21667  * Simple Hidden element used on forms 
21668  * 
21669  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21670  * 
21671  * @constructor
21672  * Creates a new Hidden form element.
21673  * @param {Object} config Configuration options
21674  */
21675
21676
21677
21678 // easy hidden field...
21679 Roo.form.Hidden = function(config){
21680     Roo.form.Hidden.superclass.constructor.call(this, config);
21681 };
21682   
21683 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21684     fieldLabel:      '',
21685     inputType:      'hidden',
21686     width:          50,
21687     allowBlank:     true,
21688     labelSeparator: '',
21689     hidden:         true,
21690     itemCls :       'x-form-item-display-none'
21691
21692
21693 });
21694
21695
21696 /*
21697  * Based on:
21698  * Ext JS Library 1.1.1
21699  * Copyright(c) 2006-2007, Ext JS, LLC.
21700  *
21701  * Originally Released Under LGPL - original licence link has changed is not relivant.
21702  *
21703  * Fork - LGPL
21704  * <script type="text/javascript">
21705  */
21706  
21707 /**
21708  * @class Roo.form.TriggerField
21709  * @extends Roo.form.TextField
21710  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21711  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21712  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21713  * for which you can provide a custom implementation.  For example:
21714  * <pre><code>
21715 var trigger = new Roo.form.TriggerField();
21716 trigger.onTriggerClick = myTriggerFn;
21717 trigger.applyTo('my-field');
21718 </code></pre>
21719  *
21720  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21721  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21722  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21723  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21724  * @constructor
21725  * Create a new TriggerField.
21726  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21727  * to the base TextField)
21728  */
21729 Roo.form.TriggerField = function(config){
21730     this.mimicing = false;
21731     Roo.form.TriggerField.superclass.constructor.call(this, config);
21732 };
21733
21734 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21735     /**
21736      * @cfg {String} triggerClass A CSS class to apply to the trigger
21737      */
21738     /**
21739      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21740      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21741      */
21742     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21743     /**
21744      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21745      */
21746     hideTrigger:false,
21747
21748     /** @cfg {Boolean} grow @hide */
21749     /** @cfg {Number} growMin @hide */
21750     /** @cfg {Number} growMax @hide */
21751
21752     /**
21753      * @hide 
21754      * @method
21755      */
21756     autoSize: Roo.emptyFn,
21757     // private
21758     monitorTab : true,
21759     // private
21760     deferHeight : true,
21761
21762     
21763     actionMode : 'wrap',
21764     // private
21765     onResize : function(w, h){
21766         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21767         if(typeof w == 'number'){
21768             var x = w - this.trigger.getWidth();
21769             this.el.setWidth(this.adjustWidth('input', x));
21770             this.trigger.setStyle('left', x+'px');
21771         }
21772     },
21773
21774     // private
21775     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21776
21777     // private
21778     getResizeEl : function(){
21779         return this.wrap;
21780     },
21781
21782     // private
21783     getPositionEl : function(){
21784         return this.wrap;
21785     },
21786
21787     // private
21788     alignErrorIcon : function(){
21789         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21790     },
21791
21792     // private
21793     onRender : function(ct, position){
21794         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21795         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21796         this.trigger = this.wrap.createChild(this.triggerConfig ||
21797                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21798         if(this.hideTrigger){
21799             this.trigger.setDisplayed(false);
21800         }
21801         this.initTrigger();
21802         if(!this.width){
21803             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21804         }
21805     },
21806
21807     // private
21808     initTrigger : function(){
21809         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21810         this.trigger.addClassOnOver('x-form-trigger-over');
21811         this.trigger.addClassOnClick('x-form-trigger-click');
21812     },
21813
21814     // private
21815     onDestroy : function(){
21816         if(this.trigger){
21817             this.trigger.removeAllListeners();
21818             this.trigger.remove();
21819         }
21820         if(this.wrap){
21821             this.wrap.remove();
21822         }
21823         Roo.form.TriggerField.superclass.onDestroy.call(this);
21824     },
21825
21826     // private
21827     onFocus : function(){
21828         Roo.form.TriggerField.superclass.onFocus.call(this);
21829         if(!this.mimicing){
21830             this.wrap.addClass('x-trigger-wrap-focus');
21831             this.mimicing = true;
21832             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21833             if(this.monitorTab){
21834                 this.el.on("keydown", this.checkTab, this);
21835             }
21836         }
21837     },
21838
21839     // private
21840     checkTab : function(e){
21841         if(e.getKey() == e.TAB){
21842             this.triggerBlur();
21843         }
21844     },
21845
21846     // private
21847     onBlur : function(){
21848         // do nothing
21849     },
21850
21851     // private
21852     mimicBlur : function(e, t){
21853         if(!this.wrap.contains(t) && this.validateBlur()){
21854             this.triggerBlur();
21855         }
21856     },
21857
21858     // private
21859     triggerBlur : function(){
21860         this.mimicing = false;
21861         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21862         if(this.monitorTab){
21863             this.el.un("keydown", this.checkTab, this);
21864         }
21865         this.wrap.removeClass('x-trigger-wrap-focus');
21866         Roo.form.TriggerField.superclass.onBlur.call(this);
21867     },
21868
21869     // private
21870     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21871     validateBlur : function(e, t){
21872         return true;
21873     },
21874
21875     // private
21876     onDisable : function(){
21877         Roo.form.TriggerField.superclass.onDisable.call(this);
21878         if(this.wrap){
21879             this.wrap.addClass('x-item-disabled');
21880         }
21881     },
21882
21883     // private
21884     onEnable : function(){
21885         Roo.form.TriggerField.superclass.onEnable.call(this);
21886         if(this.wrap){
21887             this.wrap.removeClass('x-item-disabled');
21888         }
21889     },
21890
21891     // private
21892     onShow : function(){
21893         var ae = this.getActionEl();
21894         
21895         if(ae){
21896             ae.dom.style.display = '';
21897             ae.dom.style.visibility = 'visible';
21898         }
21899     },
21900
21901     // private
21902     
21903     onHide : function(){
21904         var ae = this.getActionEl();
21905         ae.dom.style.display = 'none';
21906     },
21907
21908     /**
21909      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21910      * by an implementing function.
21911      * @method
21912      * @param {EventObject} e
21913      */
21914     onTriggerClick : Roo.emptyFn
21915 });
21916
21917 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21918 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21919 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21920 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21921     initComponent : function(){
21922         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21923
21924         this.triggerConfig = {
21925             tag:'span', cls:'x-form-twin-triggers', cn:[
21926             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21927             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21928         ]};
21929     },
21930
21931     getTrigger : function(index){
21932         return this.triggers[index];
21933     },
21934
21935     initTrigger : function(){
21936         var ts = this.trigger.select('.x-form-trigger', true);
21937         this.wrap.setStyle('overflow', 'hidden');
21938         var triggerField = this;
21939         ts.each(function(t, all, index){
21940             t.hide = function(){
21941                 var w = triggerField.wrap.getWidth();
21942                 this.dom.style.display = 'none';
21943                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21944             };
21945             t.show = function(){
21946                 var w = triggerField.wrap.getWidth();
21947                 this.dom.style.display = '';
21948                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21949             };
21950             var triggerIndex = 'Trigger'+(index+1);
21951
21952             if(this['hide'+triggerIndex]){
21953                 t.dom.style.display = 'none';
21954             }
21955             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21956             t.addClassOnOver('x-form-trigger-over');
21957             t.addClassOnClick('x-form-trigger-click');
21958         }, this);
21959         this.triggers = ts.elements;
21960     },
21961
21962     onTrigger1Click : Roo.emptyFn,
21963     onTrigger2Click : Roo.emptyFn
21964 });/*
21965  * Based on:
21966  * Ext JS Library 1.1.1
21967  * Copyright(c) 2006-2007, Ext JS, LLC.
21968  *
21969  * Originally Released Under LGPL - original licence link has changed is not relivant.
21970  *
21971  * Fork - LGPL
21972  * <script type="text/javascript">
21973  */
21974  
21975 /**
21976  * @class Roo.form.TextArea
21977  * @extends Roo.form.TextField
21978  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21979  * support for auto-sizing.
21980  * @constructor
21981  * Creates a new TextArea
21982  * @param {Object} config Configuration options
21983  */
21984 Roo.form.TextArea = function(config){
21985     Roo.form.TextArea.superclass.constructor.call(this, config);
21986     // these are provided exchanges for backwards compat
21987     // minHeight/maxHeight were replaced by growMin/growMax to be
21988     // compatible with TextField growing config values
21989     if(this.minHeight !== undefined){
21990         this.growMin = this.minHeight;
21991     }
21992     if(this.maxHeight !== undefined){
21993         this.growMax = this.maxHeight;
21994     }
21995 };
21996
21997 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21998     /**
21999      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22000      */
22001     growMin : 60,
22002     /**
22003      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22004      */
22005     growMax: 1000,
22006     /**
22007      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22008      * in the field (equivalent to setting overflow: hidden, defaults to false)
22009      */
22010     preventScrollbars: false,
22011     /**
22012      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22013      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22014      */
22015
22016     // private
22017     onRender : function(ct, position){
22018         if(!this.el){
22019             this.defaultAutoCreate = {
22020                 tag: "textarea",
22021                 style:"width:300px;height:60px;",
22022                 autocomplete: "off"
22023             };
22024         }
22025         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22026         if(this.grow){
22027             this.textSizeEl = Roo.DomHelper.append(document.body, {
22028                 tag: "pre", cls: "x-form-grow-sizer"
22029             });
22030             if(this.preventScrollbars){
22031                 this.el.setStyle("overflow", "hidden");
22032             }
22033             this.el.setHeight(this.growMin);
22034         }
22035     },
22036
22037     onDestroy : function(){
22038         if(this.textSizeEl){
22039             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22040         }
22041         Roo.form.TextArea.superclass.onDestroy.call(this);
22042     },
22043
22044     // private
22045     onKeyUp : function(e){
22046         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22047             this.autoSize();
22048         }
22049     },
22050
22051     /**
22052      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22053      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22054      */
22055     autoSize : function(){
22056         if(!this.grow || !this.textSizeEl){
22057             return;
22058         }
22059         var el = this.el;
22060         var v = el.dom.value;
22061         var ts = this.textSizeEl;
22062
22063         ts.innerHTML = '';
22064         ts.appendChild(document.createTextNode(v));
22065         v = ts.innerHTML;
22066
22067         Roo.fly(ts).setWidth(this.el.getWidth());
22068         if(v.length < 1){
22069             v = "&#160;&#160;";
22070         }else{
22071             if(Roo.isIE){
22072                 v = v.replace(/\n/g, '<p>&#160;</p>');
22073             }
22074             v += "&#160;\n&#160;";
22075         }
22076         ts.innerHTML = v;
22077         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22078         if(h != this.lastHeight){
22079             this.lastHeight = h;
22080             this.el.setHeight(h);
22081             this.fireEvent("autosize", this, h);
22082         }
22083     }
22084 });/*
22085  * Based on:
22086  * Ext JS Library 1.1.1
22087  * Copyright(c) 2006-2007, Ext JS, LLC.
22088  *
22089  * Originally Released Under LGPL - original licence link has changed is not relivant.
22090  *
22091  * Fork - LGPL
22092  * <script type="text/javascript">
22093  */
22094  
22095
22096 /**
22097  * @class Roo.form.NumberField
22098  * @extends Roo.form.TextField
22099  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22100  * @constructor
22101  * Creates a new NumberField
22102  * @param {Object} config Configuration options
22103  */
22104 Roo.form.NumberField = function(config){
22105     Roo.form.NumberField.superclass.constructor.call(this, config);
22106 };
22107
22108 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22109     /**
22110      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22111      */
22112     fieldClass: "x-form-field x-form-num-field",
22113     /**
22114      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22115      */
22116     allowDecimals : true,
22117     /**
22118      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22119      */
22120     decimalSeparator : ".",
22121     /**
22122      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22123      */
22124     decimalPrecision : 2,
22125     /**
22126      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22127      */
22128     allowNegative : true,
22129     /**
22130      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22131      */
22132     minValue : Number.NEGATIVE_INFINITY,
22133     /**
22134      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22135      */
22136     maxValue : Number.MAX_VALUE,
22137     /**
22138      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22139      */
22140     minText : "The minimum value for this field is {0}",
22141     /**
22142      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22143      */
22144     maxText : "The maximum value for this field is {0}",
22145     /**
22146      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22147      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22148      */
22149     nanText : "{0} is not a valid number",
22150
22151     // private
22152     initEvents : function(){
22153         Roo.form.NumberField.superclass.initEvents.call(this);
22154         var allowed = "0123456789";
22155         if(this.allowDecimals){
22156             allowed += this.decimalSeparator;
22157         }
22158         if(this.allowNegative){
22159             allowed += "-";
22160         }
22161         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22162         var keyPress = function(e){
22163             var k = e.getKey();
22164             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22165                 return;
22166             }
22167             var c = e.getCharCode();
22168             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22169                 e.stopEvent();
22170             }
22171         };
22172         this.el.on("keypress", keyPress, this);
22173     },
22174
22175     // private
22176     validateValue : function(value){
22177         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22178             return false;
22179         }
22180         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22181              return true;
22182         }
22183         var num = this.parseValue(value);
22184         if(isNaN(num)){
22185             this.markInvalid(String.format(this.nanText, value));
22186             return false;
22187         }
22188         if(num < this.minValue){
22189             this.markInvalid(String.format(this.minText, this.minValue));
22190             return false;
22191         }
22192         if(num > this.maxValue){
22193             this.markInvalid(String.format(this.maxText, this.maxValue));
22194             return false;
22195         }
22196         return true;
22197     },
22198
22199     getValue : function(){
22200         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22201     },
22202
22203     // private
22204     parseValue : function(value){
22205         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22206         return isNaN(value) ? '' : value;
22207     },
22208
22209     // private
22210     fixPrecision : function(value){
22211         var nan = isNaN(value);
22212         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22213             return nan ? '' : value;
22214         }
22215         return parseFloat(value).toFixed(this.decimalPrecision);
22216     },
22217
22218     setValue : function(v){
22219         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22220     },
22221
22222     // private
22223     decimalPrecisionFcn : function(v){
22224         return Math.floor(v);
22225     },
22226
22227     beforeBlur : function(){
22228         var v = this.parseValue(this.getRawValue());
22229         if(v){
22230             this.setValue(this.fixPrecision(v));
22231         }
22232     }
22233 });/*
22234  * Based on:
22235  * Ext JS Library 1.1.1
22236  * Copyright(c) 2006-2007, Ext JS, LLC.
22237  *
22238  * Originally Released Under LGPL - original licence link has changed is not relivant.
22239  *
22240  * Fork - LGPL
22241  * <script type="text/javascript">
22242  */
22243  
22244 /**
22245  * @class Roo.form.DateField
22246  * @extends Roo.form.TriggerField
22247  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22248 * @constructor
22249 * Create a new DateField
22250 * @param {Object} config
22251  */
22252 Roo.form.DateField = function(config){
22253     Roo.form.DateField.superclass.constructor.call(this, config);
22254     
22255       this.addEvents({
22256          
22257         /**
22258          * @event select
22259          * Fires when a date is selected
22260              * @param {Roo.form.DateField} combo This combo box
22261              * @param {Date} date The date selected
22262              */
22263         'select' : true
22264          
22265     });
22266     
22267     
22268     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22269     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22270     this.ddMatch = null;
22271     if(this.disabledDates){
22272         var dd = this.disabledDates;
22273         var re = "(?:";
22274         for(var i = 0; i < dd.length; i++){
22275             re += dd[i];
22276             if(i != dd.length-1) re += "|";
22277         }
22278         this.ddMatch = new RegExp(re + ")");
22279     }
22280 };
22281
22282 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22283     /**
22284      * @cfg {String} format
22285      * The default date format string which can be overriden for localization support.  The format must be
22286      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22287      */
22288     format : "m/d/y",
22289     /**
22290      * @cfg {String} altFormats
22291      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22292      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22293      */
22294     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22295     /**
22296      * @cfg {Array} disabledDays
22297      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22298      */
22299     disabledDays : null,
22300     /**
22301      * @cfg {String} disabledDaysText
22302      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22303      */
22304     disabledDaysText : "Disabled",
22305     /**
22306      * @cfg {Array} disabledDates
22307      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22308      * expression so they are very powerful. Some examples:
22309      * <ul>
22310      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22311      * <li>["03/08", "09/16"] would disable those days for every year</li>
22312      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22313      * <li>["03/../2006"] would disable every day in March 2006</li>
22314      * <li>["^03"] would disable every day in every March</li>
22315      * </ul>
22316      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22317      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22318      */
22319     disabledDates : null,
22320     /**
22321      * @cfg {String} disabledDatesText
22322      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22323      */
22324     disabledDatesText : "Disabled",
22325     /**
22326      * @cfg {Date/String} minValue
22327      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22328      * valid format (defaults to null).
22329      */
22330     minValue : null,
22331     /**
22332      * @cfg {Date/String} maxValue
22333      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22334      * valid format (defaults to null).
22335      */
22336     maxValue : null,
22337     /**
22338      * @cfg {String} minText
22339      * The error text to display when the date in the cell is before minValue (defaults to
22340      * 'The date in this field must be after {minValue}').
22341      */
22342     minText : "The date in this field must be equal to or after {0}",
22343     /**
22344      * @cfg {String} maxText
22345      * The error text to display when the date in the cell is after maxValue (defaults to
22346      * 'The date in this field must be before {maxValue}').
22347      */
22348     maxText : "The date in this field must be equal to or before {0}",
22349     /**
22350      * @cfg {String} invalidText
22351      * The error text to display when the date in the field is invalid (defaults to
22352      * '{value} is not a valid date - it must be in the format {format}').
22353      */
22354     invalidText : "{0} is not a valid date - it must be in the format {1}",
22355     /**
22356      * @cfg {String} triggerClass
22357      * An additional CSS class used to style the trigger button.  The trigger will always get the
22358      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22359      * which displays a calendar icon).
22360      */
22361     triggerClass : 'x-form-date-trigger',
22362     
22363
22364     /**
22365      * @cfg {bool} useIso
22366      * if enabled, then the date field will use a hidden field to store the 
22367      * real value as iso formated date. default (false)
22368      */ 
22369     useIso : false,
22370     /**
22371      * @cfg {String/Object} autoCreate
22372      * A DomHelper element spec, or true for a default element spec (defaults to
22373      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22374      */ 
22375     // private
22376     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22377     
22378     // private
22379     hiddenField: false,
22380     
22381     onRender : function(ct, position)
22382     {
22383         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22384         if (this.useIso) {
22385             this.el.dom.removeAttribute('name'); 
22386             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22387                     'before', true);
22388             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22389             // prevent input submission
22390             this.hiddenName = this.name;
22391         }
22392             
22393             
22394     },
22395     
22396     // private
22397     validateValue : function(value)
22398     {
22399         value = this.formatDate(value);
22400         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22401             return false;
22402         }
22403         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22404              return true;
22405         }
22406         var svalue = value;
22407         value = this.parseDate(value);
22408         if(!value){
22409             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22410             return false;
22411         }
22412         var time = value.getTime();
22413         if(this.minValue && time < this.minValue.getTime()){
22414             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22415             return false;
22416         }
22417         if(this.maxValue && time > this.maxValue.getTime()){
22418             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22419             return false;
22420         }
22421         if(this.disabledDays){
22422             var day = value.getDay();
22423             for(var i = 0; i < this.disabledDays.length; i++) {
22424                 if(day === this.disabledDays[i]){
22425                     this.markInvalid(this.disabledDaysText);
22426                     return false;
22427                 }
22428             }
22429         }
22430         var fvalue = this.formatDate(value);
22431         if(this.ddMatch && this.ddMatch.test(fvalue)){
22432             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22433             return false;
22434         }
22435         return true;
22436     },
22437
22438     // private
22439     // Provides logic to override the default TriggerField.validateBlur which just returns true
22440     validateBlur : function(){
22441         return !this.menu || !this.menu.isVisible();
22442     },
22443
22444     /**
22445      * Returns the current date value of the date field.
22446      * @return {Date} The date value
22447      */
22448     getValue : function(){
22449         
22450         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22451     },
22452
22453     /**
22454      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22455      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22456      * (the default format used is "m/d/y").
22457      * <br />Usage:
22458      * <pre><code>
22459 //All of these calls set the same date value (May 4, 2006)
22460
22461 //Pass a date object:
22462 var dt = new Date('5/4/06');
22463 dateField.setValue(dt);
22464
22465 //Pass a date string (default format):
22466 dateField.setValue('5/4/06');
22467
22468 //Pass a date string (custom format):
22469 dateField.format = 'Y-m-d';
22470 dateField.setValue('2006-5-4');
22471 </code></pre>
22472      * @param {String/Date} date The date or valid date string
22473      */
22474     setValue : function(date){
22475         if (this.hiddenField) {
22476             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22477         }
22478         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22479     },
22480
22481     // private
22482     parseDate : function(value){
22483         if(!value || value instanceof Date){
22484             return value;
22485         }
22486         var v = Date.parseDate(value, this.format);
22487         if(!v && this.altFormats){
22488             if(!this.altFormatsArray){
22489                 this.altFormatsArray = this.altFormats.split("|");
22490             }
22491             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22492                 v = Date.parseDate(value, this.altFormatsArray[i]);
22493             }
22494         }
22495         return v;
22496     },
22497
22498     // private
22499     formatDate : function(date, fmt){
22500         return (!date || !(date instanceof Date)) ?
22501                date : date.dateFormat(fmt || this.format);
22502     },
22503
22504     // private
22505     menuListeners : {
22506         select: function(m, d){
22507             this.setValue(d);
22508             this.fireEvent('select', this, d);
22509         },
22510         show : function(){ // retain focus styling
22511             this.onFocus();
22512         },
22513         hide : function(){
22514             this.focus.defer(10, this);
22515             var ml = this.menuListeners;
22516             this.menu.un("select", ml.select,  this);
22517             this.menu.un("show", ml.show,  this);
22518             this.menu.un("hide", ml.hide,  this);
22519         }
22520     },
22521
22522     // private
22523     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22524     onTriggerClick : function(){
22525         if(this.disabled){
22526             return;
22527         }
22528         if(this.menu == null){
22529             this.menu = new Roo.menu.DateMenu();
22530         }
22531         Roo.apply(this.menu.picker,  {
22532             showClear: this.allowBlank,
22533             minDate : this.minValue,
22534             maxDate : this.maxValue,
22535             disabledDatesRE : this.ddMatch,
22536             disabledDatesText : this.disabledDatesText,
22537             disabledDays : this.disabledDays,
22538             disabledDaysText : this.disabledDaysText,
22539             format : this.format,
22540             minText : String.format(this.minText, this.formatDate(this.minValue)),
22541             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22542         });
22543         this.menu.on(Roo.apply({}, this.menuListeners, {
22544             scope:this
22545         }));
22546         this.menu.picker.setValue(this.getValue() || new Date());
22547         this.menu.show(this.el, "tl-bl?");
22548     },
22549
22550     beforeBlur : function(){
22551         var v = this.parseDate(this.getRawValue());
22552         if(v){
22553             this.setValue(v);
22554         }
22555     }
22556
22557     /** @cfg {Boolean} grow @hide */
22558     /** @cfg {Number} growMin @hide */
22559     /** @cfg {Number} growMax @hide */
22560     /**
22561      * @hide
22562      * @method autoSize
22563      */
22564 });/*
22565  * Based on:
22566  * Ext JS Library 1.1.1
22567  * Copyright(c) 2006-2007, Ext JS, LLC.
22568  *
22569  * Originally Released Under LGPL - original licence link has changed is not relivant.
22570  *
22571  * Fork - LGPL
22572  * <script type="text/javascript">
22573  */
22574  
22575
22576 /**
22577  * @class Roo.form.ComboBox
22578  * @extends Roo.form.TriggerField
22579  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22580  * @constructor
22581  * Create a new ComboBox.
22582  * @param {Object} config Configuration options
22583  */
22584 Roo.form.ComboBox = function(config){
22585     Roo.form.ComboBox.superclass.constructor.call(this, config);
22586     this.addEvents({
22587         /**
22588          * @event expand
22589          * Fires when the dropdown list is expanded
22590              * @param {Roo.form.ComboBox} combo This combo box
22591              */
22592         'expand' : true,
22593         /**
22594          * @event collapse
22595          * Fires when the dropdown list is collapsed
22596              * @param {Roo.form.ComboBox} combo This combo box
22597              */
22598         'collapse' : true,
22599         /**
22600          * @event beforeselect
22601          * Fires before a list item is selected. Return false to cancel the selection.
22602              * @param {Roo.form.ComboBox} combo This combo box
22603              * @param {Roo.data.Record} record The data record returned from the underlying store
22604              * @param {Number} index The index of the selected item in the dropdown list
22605              */
22606         'beforeselect' : true,
22607         /**
22608          * @event select
22609          * Fires when a list item is selected
22610              * @param {Roo.form.ComboBox} combo This combo box
22611              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22612              * @param {Number} index The index of the selected item in the dropdown list
22613              */
22614         'select' : true,
22615         /**
22616          * @event beforequery
22617          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22618          * The event object passed has these properties:
22619              * @param {Roo.form.ComboBox} combo This combo box
22620              * @param {String} query The query
22621              * @param {Boolean} forceAll true to force "all" query
22622              * @param {Boolean} cancel true to cancel the query
22623              * @param {Object} e The query event object
22624              */
22625         'beforequery': true,
22626          /**
22627          * @event add
22628          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22629              * @param {Roo.form.ComboBox} combo This combo box
22630              */
22631         'add' : true,
22632         /**
22633          * @event edit
22634          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22635              * @param {Roo.form.ComboBox} combo This combo box
22636              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22637              */
22638         'edit' : true
22639         
22640         
22641     });
22642     if(this.transform){
22643         this.allowDomMove = false;
22644         var s = Roo.getDom(this.transform);
22645         if(!this.hiddenName){
22646             this.hiddenName = s.name;
22647         }
22648         if(!this.store){
22649             this.mode = 'local';
22650             var d = [], opts = s.options;
22651             for(var i = 0, len = opts.length;i < len; i++){
22652                 var o = opts[i];
22653                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22654                 if(o.selected) {
22655                     this.value = value;
22656                 }
22657                 d.push([value, o.text]);
22658             }
22659             this.store = new Roo.data.SimpleStore({
22660                 'id': 0,
22661                 fields: ['value', 'text'],
22662                 data : d
22663             });
22664             this.valueField = 'value';
22665             this.displayField = 'text';
22666         }
22667         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22668         if(!this.lazyRender){
22669             this.target = true;
22670             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22671             s.parentNode.removeChild(s); // remove it
22672             this.render(this.el.parentNode);
22673         }else{
22674             s.parentNode.removeChild(s); // remove it
22675         }
22676
22677     }
22678     if (this.store) {
22679         this.store = Roo.factory(this.store, Roo.data);
22680     }
22681     
22682     this.selectedIndex = -1;
22683     if(this.mode == 'local'){
22684         if(config.queryDelay === undefined){
22685             this.queryDelay = 10;
22686         }
22687         if(config.minChars === undefined){
22688             this.minChars = 0;
22689         }
22690     }
22691 };
22692
22693 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22694     /**
22695      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22696      */
22697     /**
22698      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22699      * rendering into an Roo.Editor, defaults to false)
22700      */
22701     /**
22702      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22703      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22704      */
22705     /**
22706      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22707      */
22708     /**
22709      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22710      * the dropdown list (defaults to undefined, with no header element)
22711      */
22712
22713      /**
22714      * @cfg {String/Roo.Template} tpl The template to use to render the output
22715      */
22716      
22717     // private
22718     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22719     /**
22720      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22721      */
22722     listWidth: undefined,
22723     /**
22724      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22725      * mode = 'remote' or 'text' if mode = 'local')
22726      */
22727     displayField: undefined,
22728     /**
22729      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22730      * mode = 'remote' or 'value' if mode = 'local'). 
22731      * Note: use of a valueField requires the user make a selection
22732      * in order for a value to be mapped.
22733      */
22734     valueField: undefined,
22735     /**
22736      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22737      * field's data value (defaults to the underlying DOM element's name)
22738      */
22739     hiddenName: undefined,
22740     /**
22741      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22742      */
22743     listClass: '',
22744     /**
22745      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22746      */
22747     selectedClass: 'x-combo-selected',
22748     /**
22749      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22750      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22751      * which displays a downward arrow icon).
22752      */
22753     triggerClass : 'x-form-arrow-trigger',
22754     /**
22755      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22756      */
22757     shadow:'sides',
22758     /**
22759      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22760      * anchor positions (defaults to 'tl-bl')
22761      */
22762     listAlign: 'tl-bl?',
22763     /**
22764      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22765      */
22766     maxHeight: 300,
22767     /**
22768      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22769      * query specified by the allQuery config option (defaults to 'query')
22770      */
22771     triggerAction: 'query',
22772     /**
22773      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22774      * (defaults to 4, does not apply if editable = false)
22775      */
22776     minChars : 4,
22777     /**
22778      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22779      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22780      */
22781     typeAhead: false,
22782     /**
22783      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22784      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22785      */
22786     queryDelay: 500,
22787     /**
22788      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22789      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22790      */
22791     pageSize: 0,
22792     /**
22793      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22794      * when editable = true (defaults to false)
22795      */
22796     selectOnFocus:false,
22797     /**
22798      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22799      */
22800     queryParam: 'query',
22801     /**
22802      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22803      * when mode = 'remote' (defaults to 'Loading...')
22804      */
22805     loadingText: 'Loading...',
22806     /**
22807      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22808      */
22809     resizable: false,
22810     /**
22811      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22812      */
22813     handleHeight : 8,
22814     /**
22815      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22816      * traditional select (defaults to true)
22817      */
22818     editable: true,
22819     /**
22820      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22821      */
22822     allQuery: '',
22823     /**
22824      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22825      */
22826     mode: 'remote',
22827     /**
22828      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22829      * listWidth has a higher value)
22830      */
22831     minListWidth : 70,
22832     /**
22833      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22834      * allow the user to set arbitrary text into the field (defaults to false)
22835      */
22836     forceSelection:false,
22837     /**
22838      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22839      * if typeAhead = true (defaults to 250)
22840      */
22841     typeAheadDelay : 250,
22842     /**
22843      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22844      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22845      */
22846     valueNotFoundText : undefined,
22847     /**
22848      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22849      */
22850     blockFocus : false,
22851     
22852     /**
22853      * @cfg {Boolean} disableClear Disable showing of clear button.
22854      */
22855     disableClear : false,
22856     /**
22857      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22858      */
22859     alwaysQuery : false,
22860     
22861     //private
22862     addicon : false,
22863     editicon: false,
22864     
22865     
22866     // private
22867     onRender : function(ct, position){
22868         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22869         if(this.hiddenName){
22870             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22871                     'before', true);
22872             this.hiddenField.value =
22873                 this.hiddenValue !== undefined ? this.hiddenValue :
22874                 this.value !== undefined ? this.value : '';
22875
22876             // prevent input submission
22877             this.el.dom.removeAttribute('name');
22878         }
22879         if(Roo.isGecko){
22880             this.el.dom.setAttribute('autocomplete', 'off');
22881         }
22882
22883         var cls = 'x-combo-list';
22884
22885         this.list = new Roo.Layer({
22886             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22887         });
22888
22889         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22890         this.list.setWidth(lw);
22891         this.list.swallowEvent('mousewheel');
22892         this.assetHeight = 0;
22893
22894         if(this.title){
22895             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22896             this.assetHeight += this.header.getHeight();
22897         }
22898
22899         this.innerList = this.list.createChild({cls:cls+'-inner'});
22900         this.innerList.on('mouseover', this.onViewOver, this);
22901         this.innerList.on('mousemove', this.onViewMove, this);
22902         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22903         
22904         if(this.allowBlank && !this.pageSize && !this.disableClear){
22905             this.footer = this.list.createChild({cls:cls+'-ft'});
22906             this.pageTb = new Roo.Toolbar(this.footer);
22907            
22908         }
22909         if(this.pageSize){
22910             this.footer = this.list.createChild({cls:cls+'-ft'});
22911             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22912                     {pageSize: this.pageSize});
22913             
22914         }
22915         
22916         if (this.pageTb && this.allowBlank && !this.disableClear) {
22917             var _this = this;
22918             this.pageTb.add(new Roo.Toolbar.Fill(), {
22919                 cls: 'x-btn-icon x-btn-clear',
22920                 text: '&#160;',
22921                 handler: function()
22922                 {
22923                     _this.collapse();
22924                     _this.clearValue();
22925                     _this.onSelect(false, -1);
22926                 }
22927             });
22928         }
22929         if (this.footer) {
22930             this.assetHeight += this.footer.getHeight();
22931         }
22932         
22933
22934         if(!this.tpl){
22935             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22936         }
22937
22938         this.view = new Roo.View(this.innerList, this.tpl, {
22939             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22940         });
22941
22942         this.view.on('click', this.onViewClick, this);
22943
22944         this.store.on('beforeload', this.onBeforeLoad, this);
22945         this.store.on('load', this.onLoad, this);
22946         this.store.on('loadexception', this.collapse, this);
22947
22948         if(this.resizable){
22949             this.resizer = new Roo.Resizable(this.list,  {
22950                pinned:true, handles:'se'
22951             });
22952             this.resizer.on('resize', function(r, w, h){
22953                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22954                 this.listWidth = w;
22955                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22956                 this.restrictHeight();
22957             }, this);
22958             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22959         }
22960         if(!this.editable){
22961             this.editable = true;
22962             this.setEditable(false);
22963         }  
22964         
22965         
22966         if (typeof(this.events.add.listeners) != 'undefined') {
22967             
22968             this.addicon = this.wrap.createChild(
22969                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
22970        
22971             this.addicon.on('click', function(e) {
22972                 this.fireEvent('add', this);
22973             }, this);
22974         }
22975         if (typeof(this.events.edit.listeners) != 'undefined') {
22976             
22977             this.editicon = this.wrap.createChild(
22978                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
22979             if (this.addicon) {
22980                 this.editicon.setStyle('margin-left', '40px');
22981             }
22982             this.editicon.on('click', function(e) {
22983                 
22984                 // we fire even  if inothing is selected..
22985                 this.fireEvent('edit', this, this.lastData );
22986                 
22987             }, this);
22988         }
22989         
22990         
22991         
22992     },
22993
22994     // private
22995     initEvents : function(){
22996         Roo.form.ComboBox.superclass.initEvents.call(this);
22997
22998         this.keyNav = new Roo.KeyNav(this.el, {
22999             "up" : function(e){
23000                 this.inKeyMode = true;
23001                 this.selectPrev();
23002             },
23003
23004             "down" : function(e){
23005                 if(!this.isExpanded()){
23006                     this.onTriggerClick();
23007                 }else{
23008                     this.inKeyMode = true;
23009                     this.selectNext();
23010                 }
23011             },
23012
23013             "enter" : function(e){
23014                 this.onViewClick();
23015                 //return true;
23016             },
23017
23018             "esc" : function(e){
23019                 this.collapse();
23020             },
23021
23022             "tab" : function(e){
23023                 this.onViewClick(false);
23024                 return true;
23025             },
23026
23027             scope : this,
23028
23029             doRelay : function(foo, bar, hname){
23030                 if(hname == 'down' || this.scope.isExpanded()){
23031                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23032                 }
23033                 return true;
23034             },
23035
23036             forceKeyDown: true
23037         });
23038         this.queryDelay = Math.max(this.queryDelay || 10,
23039                 this.mode == 'local' ? 10 : 250);
23040         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23041         if(this.typeAhead){
23042             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23043         }
23044         if(this.editable !== false){
23045             this.el.on("keyup", this.onKeyUp, this);
23046         }
23047         if(this.forceSelection){
23048             this.on('blur', this.doForce, this);
23049         }
23050     },
23051
23052     onDestroy : function(){
23053         if(this.view){
23054             this.view.setStore(null);
23055             this.view.el.removeAllListeners();
23056             this.view.el.remove();
23057             this.view.purgeListeners();
23058         }
23059         if(this.list){
23060             this.list.destroy();
23061         }
23062         if(this.store){
23063             this.store.un('beforeload', this.onBeforeLoad, this);
23064             this.store.un('load', this.onLoad, this);
23065             this.store.un('loadexception', this.collapse, this);
23066         }
23067         Roo.form.ComboBox.superclass.onDestroy.call(this);
23068     },
23069
23070     // private
23071     fireKey : function(e){
23072         if(e.isNavKeyPress() && !this.list.isVisible()){
23073             this.fireEvent("specialkey", this, e);
23074         }
23075     },
23076
23077     // private
23078     onResize: function(w, h){
23079         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23080         
23081         if(typeof w != 'number'){
23082             // we do not handle it!?!?
23083             return;
23084         }
23085         var tw = this.trigger.getWidth();
23086         tw += this.addicon ? this.addicon.getWidth() : 0;
23087         tw += this.editicon ? this.editicon.getWidth() : 0;
23088         var x = w - tw;
23089         this.el.setWidth( this.adjustWidth('input', x));
23090             
23091         this.trigger.setStyle('left', x+'px');
23092         
23093         if(this.list && this.listWidth === undefined){
23094             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23095             this.list.setWidth(lw);
23096             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23097         }
23098         
23099     
23100         
23101     },
23102
23103     /**
23104      * Allow or prevent the user from directly editing the field text.  If false is passed,
23105      * the user will only be able to select from the items defined in the dropdown list.  This method
23106      * is the runtime equivalent of setting the 'editable' config option at config time.
23107      * @param {Boolean} value True to allow the user to directly edit the field text
23108      */
23109     setEditable : function(value){
23110         if(value == this.editable){
23111             return;
23112         }
23113         this.editable = value;
23114         if(!value){
23115             this.el.dom.setAttribute('readOnly', true);
23116             this.el.on('mousedown', this.onTriggerClick,  this);
23117             this.el.addClass('x-combo-noedit');
23118         }else{
23119             this.el.dom.setAttribute('readOnly', false);
23120             this.el.un('mousedown', this.onTriggerClick,  this);
23121             this.el.removeClass('x-combo-noedit');
23122         }
23123     },
23124
23125     // private
23126     onBeforeLoad : function(){
23127         if(!this.hasFocus){
23128             return;
23129         }
23130         this.innerList.update(this.loadingText ?
23131                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23132         this.restrictHeight();
23133         this.selectedIndex = -1;
23134     },
23135
23136     // private
23137     onLoad : function(){
23138         if(!this.hasFocus){
23139             return;
23140         }
23141         if(this.store.getCount() > 0){
23142             this.expand();
23143             this.restrictHeight();
23144             if(this.lastQuery == this.allQuery){
23145                 if(this.editable){
23146                     this.el.dom.select();
23147                 }
23148                 if(!this.selectByValue(this.value, true)){
23149                     this.select(0, true);
23150                 }
23151             }else{
23152                 this.selectNext();
23153                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23154                     this.taTask.delay(this.typeAheadDelay);
23155                 }
23156             }
23157         }else{
23158             this.onEmptyResults();
23159         }
23160         //this.el.focus();
23161     },
23162
23163     // private
23164     onTypeAhead : function(){
23165         if(this.store.getCount() > 0){
23166             var r = this.store.getAt(0);
23167             var newValue = r.data[this.displayField];
23168             var len = newValue.length;
23169             var selStart = this.getRawValue().length;
23170             if(selStart != len){
23171                 this.setRawValue(newValue);
23172                 this.selectText(selStart, newValue.length);
23173             }
23174         }
23175     },
23176
23177     // private
23178     onSelect : function(record, index){
23179         if(this.fireEvent('beforeselect', this, record, index) !== false){
23180             this.setFromData(index > -1 ? record.data : false);
23181             this.collapse();
23182             this.fireEvent('select', this, record, index);
23183         }
23184     },
23185
23186     /**
23187      * Returns the currently selected field value or empty string if no value is set.
23188      * @return {String} value The selected value
23189      */
23190     getValue : function(){
23191         if(this.valueField){
23192             return typeof this.value != 'undefined' ? this.value : '';
23193         }else{
23194             return Roo.form.ComboBox.superclass.getValue.call(this);
23195         }
23196     },
23197
23198     /**
23199      * Clears any text/value currently set in the field
23200      */
23201     clearValue : function(){
23202         if(this.hiddenField){
23203             this.hiddenField.value = '';
23204         }
23205         this.value = '';
23206         this.setRawValue('');
23207         this.lastSelectionText = '';
23208         this.applyEmptyText();
23209     },
23210
23211     /**
23212      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23213      * will be displayed in the field.  If the value does not match the data value of an existing item,
23214      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23215      * Otherwise the field will be blank (although the value will still be set).
23216      * @param {String} value The value to match
23217      */
23218     setValue : function(v){
23219         var text = v;
23220         if(this.valueField){
23221             var r = this.findRecord(this.valueField, v);
23222             if(r){
23223                 text = r.data[this.displayField];
23224             }else if(this.valueNotFoundText !== undefined){
23225                 text = this.valueNotFoundText;
23226             }
23227         }
23228         this.lastSelectionText = text;
23229         if(this.hiddenField){
23230             this.hiddenField.value = v;
23231         }
23232         Roo.form.ComboBox.superclass.setValue.call(this, text);
23233         this.value = v;
23234     },
23235     /**
23236      * @property {Object} the last set data for the element
23237      */
23238     
23239     lastData : false,
23240     /**
23241      * Sets the value of the field based on a object which is related to the record format for the store.
23242      * @param {Object} value the value to set as. or false on reset?
23243      */
23244     setFromData : function(o){
23245         var dv = ''; // display value
23246         var vv = ''; // value value..
23247         this.lastData = o;
23248         if (this.displayField) {
23249             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23250         } else {
23251             // this is an error condition!!!
23252             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23253         }
23254         
23255         if(this.valueField){
23256             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23257         }
23258         if(this.hiddenField){
23259             this.hiddenField.value = vv;
23260             
23261             this.lastSelectionText = dv;
23262             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23263             this.value = vv;
23264             return;
23265         }
23266         // no hidden field.. - we store the value in 'value', but still display
23267         // display field!!!!
23268         this.lastSelectionText = dv;
23269         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23270         this.value = vv;
23271         
23272         
23273     },
23274     // private
23275     reset : function(){
23276         // overridden so that last data is reset..
23277         this.setValue(this.originalValue);
23278         this.clearInvalid();
23279         this.lastData = false;
23280     },
23281     // private
23282     findRecord : function(prop, value){
23283         var record;
23284         if(this.store.getCount() > 0){
23285             this.store.each(function(r){
23286                 if(r.data[prop] == value){
23287                     record = r;
23288                     return false;
23289                 }
23290             });
23291         }
23292         return record;
23293     },
23294
23295     // private
23296     onViewMove : function(e, t){
23297         this.inKeyMode = false;
23298     },
23299
23300     // private
23301     onViewOver : function(e, t){
23302         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23303             return;
23304         }
23305         var item = this.view.findItemFromChild(t);
23306         if(item){
23307             var index = this.view.indexOf(item);
23308             this.select(index, false);
23309         }
23310     },
23311
23312     // private
23313     onViewClick : function(doFocus){
23314         var index = this.view.getSelectedIndexes()[0];
23315         var r = this.store.getAt(index);
23316         if(r){
23317             this.onSelect(r, index);
23318         }
23319         if(doFocus !== false && !this.blockFocus){
23320             this.el.focus();
23321         }
23322     },
23323
23324     // private
23325     restrictHeight : function(){
23326         this.innerList.dom.style.height = '';
23327         var inner = this.innerList.dom;
23328         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23329         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23330         this.list.beginUpdate();
23331         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23332         this.list.alignTo(this.el, this.listAlign);
23333         this.list.endUpdate();
23334     },
23335
23336     // private
23337     onEmptyResults : function(){
23338         this.collapse();
23339     },
23340
23341     /**
23342      * Returns true if the dropdown list is expanded, else false.
23343      */
23344     isExpanded : function(){
23345         return this.list.isVisible();
23346     },
23347
23348     /**
23349      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23350      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23351      * @param {String} value The data value of the item to select
23352      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23353      * selected item if it is not currently in view (defaults to true)
23354      * @return {Boolean} True if the value matched an item in the list, else false
23355      */
23356     selectByValue : function(v, scrollIntoView){
23357         if(v !== undefined && v !== null){
23358             var r = this.findRecord(this.valueField || this.displayField, v);
23359             if(r){
23360                 this.select(this.store.indexOf(r), scrollIntoView);
23361                 return true;
23362             }
23363         }
23364         return false;
23365     },
23366
23367     /**
23368      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23369      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23370      * @param {Number} index The zero-based index of the list item to select
23371      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23372      * selected item if it is not currently in view (defaults to true)
23373      */
23374     select : function(index, scrollIntoView){
23375         this.selectedIndex = index;
23376         this.view.select(index);
23377         if(scrollIntoView !== false){
23378             var el = this.view.getNode(index);
23379             if(el){
23380                 this.innerList.scrollChildIntoView(el, false);
23381             }
23382         }
23383     },
23384
23385     // private
23386     selectNext : function(){
23387         var ct = this.store.getCount();
23388         if(ct > 0){
23389             if(this.selectedIndex == -1){
23390                 this.select(0);
23391             }else if(this.selectedIndex < ct-1){
23392                 this.select(this.selectedIndex+1);
23393             }
23394         }
23395     },
23396
23397     // private
23398     selectPrev : function(){
23399         var ct = this.store.getCount();
23400         if(ct > 0){
23401             if(this.selectedIndex == -1){
23402                 this.select(0);
23403             }else if(this.selectedIndex != 0){
23404                 this.select(this.selectedIndex-1);
23405             }
23406         }
23407     },
23408
23409     // private
23410     onKeyUp : function(e){
23411         if(this.editable !== false && !e.isSpecialKey()){
23412             this.lastKey = e.getKey();
23413             this.dqTask.delay(this.queryDelay);
23414         }
23415     },
23416
23417     // private
23418     validateBlur : function(){
23419         return !this.list || !this.list.isVisible();   
23420     },
23421
23422     // private
23423     initQuery : function(){
23424         this.doQuery(this.getRawValue());
23425     },
23426
23427     // private
23428     doForce : function(){
23429         if(this.el.dom.value.length > 0){
23430             this.el.dom.value =
23431                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23432             this.applyEmptyText();
23433         }
23434     },
23435
23436     /**
23437      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23438      * query allowing the query action to be canceled if needed.
23439      * @param {String} query The SQL query to execute
23440      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23441      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23442      * saved in the current store (defaults to false)
23443      */
23444     doQuery : function(q, forceAll){
23445         if(q === undefined || q === null){
23446             q = '';
23447         }
23448         var qe = {
23449             query: q,
23450             forceAll: forceAll,
23451             combo: this,
23452             cancel:false
23453         };
23454         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23455             return false;
23456         }
23457         q = qe.query;
23458         forceAll = qe.forceAll;
23459         if(forceAll === true || (q.length >= this.minChars)){
23460             if(this.lastQuery != q || this.alwaysQuery){
23461                 this.lastQuery = q;
23462                 if(this.mode == 'local'){
23463                     this.selectedIndex = -1;
23464                     if(forceAll){
23465                         this.store.clearFilter();
23466                     }else{
23467                         this.store.filter(this.displayField, q);
23468                     }
23469                     this.onLoad();
23470                 }else{
23471                     this.store.baseParams[this.queryParam] = q;
23472                     this.store.load({
23473                         params: this.getParams(q)
23474                     });
23475                     this.expand();
23476                 }
23477             }else{
23478                 this.selectedIndex = -1;
23479                 this.onLoad();   
23480             }
23481         }
23482     },
23483
23484     // private
23485     getParams : function(q){
23486         var p = {};
23487         //p[this.queryParam] = q;
23488         if(this.pageSize){
23489             p.start = 0;
23490             p.limit = this.pageSize;
23491         }
23492         return p;
23493     },
23494
23495     /**
23496      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23497      */
23498     collapse : function(){
23499         if(!this.isExpanded()){
23500             return;
23501         }
23502         this.list.hide();
23503         Roo.get(document).un('mousedown', this.collapseIf, this);
23504         Roo.get(document).un('mousewheel', this.collapseIf, this);
23505         if (!this.editable) {
23506             Roo.get(document).un('keydown', this.listKeyPress, this);
23507         }
23508         this.fireEvent('collapse', this);
23509     },
23510
23511     // private
23512     collapseIf : function(e){
23513         if(!e.within(this.wrap) && !e.within(this.list)){
23514             this.collapse();
23515         }
23516     },
23517
23518     /**
23519      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23520      */
23521     expand : function(){
23522         if(this.isExpanded() || !this.hasFocus){
23523             return;
23524         }
23525         this.list.alignTo(this.el, this.listAlign);
23526         this.list.show();
23527         Roo.get(document).on('mousedown', this.collapseIf, this);
23528         Roo.get(document).on('mousewheel', this.collapseIf, this);
23529         if (!this.editable) {
23530             Roo.get(document).on('keydown', this.listKeyPress, this);
23531         }
23532         
23533         this.fireEvent('expand', this);
23534     },
23535
23536     // private
23537     // Implements the default empty TriggerField.onTriggerClick function
23538     onTriggerClick : function(){
23539         if(this.disabled){
23540             return;
23541         }
23542         if(this.isExpanded()){
23543             this.collapse();
23544             if (!this.blockFocus) {
23545                 this.el.focus();
23546             }
23547             
23548         }else {
23549             this.hasFocus = true;
23550             if(this.triggerAction == 'all') {
23551                 this.doQuery(this.allQuery, true);
23552             } else {
23553                 this.doQuery(this.getRawValue());
23554             }
23555             if (!this.blockFocus) {
23556                 this.el.focus();
23557             }
23558         }
23559     },
23560     listKeyPress : function(e)
23561     {
23562         //Roo.log('listkeypress');
23563         // scroll to first matching element based on key pres..
23564         if (e.isSpecialKey()) {
23565             return false;
23566         }
23567         var k = String.fromCharCode(e.getKey()).toUpperCase();
23568         //Roo.log(k);
23569         var match  = false;
23570         var csel = this.view.getSelectedNodes();
23571         var cselitem = false;
23572         if (csel.length) {
23573             var ix = this.view.indexOf(csel[0]);
23574             cselitem  = this.store.getAt(ix);
23575             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23576                 cselitem = false;
23577             }
23578             
23579         }
23580         
23581         this.store.each(function(v) { 
23582             if (cselitem) {
23583                 // start at existing selection.
23584                 if (cselitem.id == v.id) {
23585                     cselitem = false;
23586                 }
23587                 return;
23588             }
23589                 
23590             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23591                 match = this.store.indexOf(v);
23592                 return false;
23593             }
23594         }, this);
23595         
23596         if (match === false) {
23597             return true; // no more action?
23598         }
23599         // scroll to?
23600         this.view.select(match);
23601         var sn = Roo.get(this.view.getSelectedNodes()[0])
23602         sn.scrollIntoView(sn.dom.parentNode, false);
23603     }
23604
23605     /** 
23606     * @cfg {Boolean} grow 
23607     * @hide 
23608     */
23609     /** 
23610     * @cfg {Number} growMin 
23611     * @hide 
23612     */
23613     /** 
23614     * @cfg {Number} growMax 
23615     * @hide 
23616     */
23617     /**
23618      * @hide
23619      * @method autoSize
23620      */
23621 });/*
23622  * Based on:
23623  * Ext JS Library 1.1.1
23624  * Copyright(c) 2006-2007, Ext JS, LLC.
23625  *
23626  * Originally Released Under LGPL - original licence link has changed is not relivant.
23627  *
23628  * Fork - LGPL
23629  * <script type="text/javascript">
23630  */
23631 /**
23632  * @class Roo.form.Checkbox
23633  * @extends Roo.form.Field
23634  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23635  * @constructor
23636  * Creates a new Checkbox
23637  * @param {Object} config Configuration options
23638  */
23639 Roo.form.Checkbox = function(config){
23640     Roo.form.Checkbox.superclass.constructor.call(this, config);
23641     this.addEvents({
23642         /**
23643          * @event check
23644          * Fires when the checkbox is checked or unchecked.
23645              * @param {Roo.form.Checkbox} this This checkbox
23646              * @param {Boolean} checked The new checked value
23647              */
23648         check : true
23649     });
23650 };
23651
23652 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23653     /**
23654      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23655      */
23656     focusClass : undefined,
23657     /**
23658      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23659      */
23660     fieldClass: "x-form-field",
23661     /**
23662      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23663      */
23664     checked: false,
23665     /**
23666      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23667      * {tag: "input", type: "checkbox", autocomplete: "off"})
23668      */
23669     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23670     /**
23671      * @cfg {String} boxLabel The text that appears beside the checkbox
23672      */
23673     boxLabel : "",
23674     /**
23675      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23676      */  
23677     inputValue : '1',
23678     /**
23679      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23680      */
23681      valueOff: '0', // value when not checked..
23682
23683     actionMode : 'viewEl', 
23684     //
23685     // private
23686     itemCls : 'x-menu-check-item x-form-item',
23687     groupClass : 'x-menu-group-item',
23688     inputType : 'hidden',
23689     
23690     
23691     inSetChecked: false, // check that we are not calling self...
23692     
23693     inputElement: false, // real input element?
23694     basedOn: false, // ????
23695     
23696     isFormField: true, // not sure where this is needed!!!!
23697
23698     onResize : function(){
23699         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23700         if(!this.boxLabel){
23701             this.el.alignTo(this.wrap, 'c-c');
23702         }
23703     },
23704
23705     initEvents : function(){
23706         Roo.form.Checkbox.superclass.initEvents.call(this);
23707         this.el.on("click", this.onClick,  this);
23708         this.el.on("change", this.onClick,  this);
23709     },
23710
23711
23712     getResizeEl : function(){
23713         return this.wrap;
23714     },
23715
23716     getPositionEl : function(){
23717         return this.wrap;
23718     },
23719
23720     // private
23721     onRender : function(ct, position){
23722         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23723         /*
23724         if(this.inputValue !== undefined){
23725             this.el.dom.value = this.inputValue;
23726         }
23727         */
23728         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23729         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23730         var viewEl = this.wrap.createChild({ 
23731             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23732         this.viewEl = viewEl;   
23733         this.wrap.on('click', this.onClick,  this); 
23734         
23735         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23736         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23737         
23738         
23739         
23740         if(this.boxLabel){
23741             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23742         //    viewEl.on('click', this.onClick,  this); 
23743         }
23744         //if(this.checked){
23745             this.setChecked(this.checked);
23746         //}else{
23747             //this.checked = this.el.dom;
23748         //}
23749
23750     },
23751
23752     // private
23753     initValue : Roo.emptyFn,
23754
23755     /**
23756      * Returns the checked state of the checkbox.
23757      * @return {Boolean} True if checked, else false
23758      */
23759     getValue : function(){
23760         if(this.el){
23761             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23762         }
23763         return this.valueOff;
23764         
23765     },
23766
23767         // private
23768     onClick : function(){ 
23769         this.setChecked(!this.checked);
23770
23771         //if(this.el.dom.checked != this.checked){
23772         //    this.setValue(this.el.dom.checked);
23773        // }
23774     },
23775
23776     /**
23777      * Sets the checked state of the checkbox.
23778      * On is always based on a string comparison between inputValue and the param.
23779      * @param {Boolean/String} value - the value to set 
23780      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23781      */
23782     setValue : function(v,suppressEvent){
23783         
23784         
23785         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23786         //if(this.el && this.el.dom){
23787         //    this.el.dom.checked = this.checked;
23788         //    this.el.dom.defaultChecked = this.checked;
23789         //}
23790         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23791         //this.fireEvent("check", this, this.checked);
23792     },
23793     // private..
23794     setChecked : function(state,suppressEvent)
23795     {
23796         if (this.inSetChecked) {
23797             this.checked = state;
23798             return;
23799         }
23800         
23801     
23802         if(this.wrap){
23803             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23804         }
23805         this.checked = state;
23806         if(suppressEvent !== true){
23807             this.fireEvent('check', this, state);
23808         }
23809         this.inSetChecked = true;
23810         this.el.dom.value = state ? this.inputValue : this.valueOff;
23811         this.inSetChecked = false;
23812         
23813     },
23814     // handle setting of hidden value by some other method!!?!?
23815     setFromHidden: function()
23816     {
23817         if(!this.el){
23818             return;
23819         }
23820         //console.log("SET FROM HIDDEN");
23821         //alert('setFrom hidden');
23822         this.setValue(this.el.dom.value);
23823     },
23824     
23825     onDestroy : function()
23826     {
23827         if(this.viewEl){
23828             Roo.get(this.viewEl).remove();
23829         }
23830          
23831         Roo.form.Checkbox.superclass.onDestroy.call(this);
23832     }
23833
23834 });/*
23835  * Based on:
23836  * Ext JS Library 1.1.1
23837  * Copyright(c) 2006-2007, Ext JS, LLC.
23838  *
23839  * Originally Released Under LGPL - original licence link has changed is not relivant.
23840  *
23841  * Fork - LGPL
23842  * <script type="text/javascript">
23843  */
23844  
23845 /**
23846  * @class Roo.form.Radio
23847  * @extends Roo.form.Checkbox
23848  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23849  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23850  * @constructor
23851  * Creates a new Radio
23852  * @param {Object} config Configuration options
23853  */
23854 Roo.form.Radio = function(){
23855     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23856 };
23857 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23858     inputType: 'radio',
23859
23860     /**
23861      * If this radio is part of a group, it will return the selected value
23862      * @return {String}
23863      */
23864     getGroupValue : function(){
23865         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23866     }
23867 });//<script type="text/javascript">
23868
23869 /*
23870  * Ext JS Library 1.1.1
23871  * Copyright(c) 2006-2007, Ext JS, LLC.
23872  * licensing@extjs.com
23873  * 
23874  * http://www.extjs.com/license
23875  */
23876  
23877  /*
23878   * 
23879   * Known bugs:
23880   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23881   * - IE ? - no idea how much works there.
23882   * 
23883   * 
23884   * 
23885   */
23886  
23887
23888 /**
23889  * @class Ext.form.HtmlEditor
23890  * @extends Ext.form.Field
23891  * Provides a lightweight HTML Editor component.
23892  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23893  * 
23894  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23895  * supported by this editor.</b><br/><br/>
23896  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23897  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23898  */
23899 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23900       /**
23901      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23902      */
23903     toolbars : false,
23904     /**
23905      * @cfg {String} createLinkText The default text for the create link prompt
23906      */
23907     createLinkText : 'Please enter the URL for the link:',
23908     /**
23909      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23910      */
23911     defaultLinkValue : 'http:/'+'/',
23912    
23913     
23914     // id of frame..
23915     frameId: false,
23916     
23917     // private properties
23918     validationEvent : false,
23919     deferHeight: true,
23920     initialized : false,
23921     activated : false,
23922     sourceEditMode : false,
23923     onFocus : Roo.emptyFn,
23924     iframePad:3,
23925     hideMode:'offsets',
23926     defaultAutoCreate : {
23927         tag: "textarea",
23928         style:"width:500px;height:300px;",
23929         autocomplete: "off"
23930     },
23931
23932     // private
23933     initComponent : function(){
23934         this.addEvents({
23935             /**
23936              * @event initialize
23937              * Fires when the editor is fully initialized (including the iframe)
23938              * @param {HtmlEditor} this
23939              */
23940             initialize: true,
23941             /**
23942              * @event activate
23943              * Fires when the editor is first receives the focus. Any insertion must wait
23944              * until after this event.
23945              * @param {HtmlEditor} this
23946              */
23947             activate: true,
23948              /**
23949              * @event beforesync
23950              * Fires before the textarea is updated with content from the editor iframe. Return false
23951              * to cancel the sync.
23952              * @param {HtmlEditor} this
23953              * @param {String} html
23954              */
23955             beforesync: true,
23956              /**
23957              * @event beforepush
23958              * Fires before the iframe editor is updated with content from the textarea. Return false
23959              * to cancel the push.
23960              * @param {HtmlEditor} this
23961              * @param {String} html
23962              */
23963             beforepush: true,
23964              /**
23965              * @event sync
23966              * Fires when the textarea is updated with content from the editor iframe.
23967              * @param {HtmlEditor} this
23968              * @param {String} html
23969              */
23970             sync: true,
23971              /**
23972              * @event push
23973              * Fires when the iframe editor is updated with content from the textarea.
23974              * @param {HtmlEditor} this
23975              * @param {String} html
23976              */
23977             push: true,
23978              /**
23979              * @event editmodechange
23980              * Fires when the editor switches edit modes
23981              * @param {HtmlEditor} this
23982              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23983              */
23984             editmodechange: true,
23985             /**
23986              * @event editorevent
23987              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23988              * @param {HtmlEditor} this
23989              */
23990             editorevent: true
23991         })
23992     },
23993
23994     /**
23995      * Protected method that will not generally be called directly. It
23996      * is called when the editor creates its toolbar. Override this method if you need to
23997      * add custom toolbar buttons.
23998      * @param {HtmlEditor} editor
23999      */
24000     createToolbar : function(editor){
24001         if (!editor.toolbars || !editor.toolbars.length) {
24002             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24003         }
24004         
24005         for (var i =0 ; i < editor.toolbars.length;i++) {
24006             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24007             editor.toolbars[i].init(editor);
24008         }
24009          
24010         
24011     },
24012
24013     /**
24014      * Protected method that will not generally be called directly. It
24015      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24016      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24017      */
24018     getDocMarkup : function(){
24019         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
24020     },
24021
24022     // private
24023     onRender : function(ct, position){
24024         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24025         this.el.dom.style.border = '0 none';
24026         this.el.dom.setAttribute('tabIndex', -1);
24027         this.el.addClass('x-hidden');
24028         if(Roo.isIE){ // fix IE 1px bogus margin
24029             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24030         }
24031         this.wrap = this.el.wrap({
24032             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24033         });
24034
24035         this.frameId = Roo.id();
24036         this.createToolbar(this);
24037         
24038         
24039         
24040         
24041       
24042         
24043         var iframe = this.wrap.createChild({
24044             tag: 'iframe',
24045             id: this.frameId,
24046             name: this.frameId,
24047             frameBorder : 'no',
24048             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24049         });
24050         
24051        // console.log(iframe);
24052         //this.wrap.dom.appendChild(iframe);
24053
24054         this.iframe = iframe.dom;
24055
24056          this.assignDocWin();
24057         
24058         this.doc.designMode = 'on';
24059        
24060         this.doc.open();
24061         this.doc.write(this.getDocMarkup());
24062         this.doc.close();
24063
24064         
24065         var task = { // must defer to wait for browser to be ready
24066             run : function(){
24067                 //console.log("run task?" + this.doc.readyState);
24068                 this.assignDocWin();
24069                 if(this.doc.body || this.doc.readyState == 'complete'){
24070                     try {
24071                         this.doc.designMode="on";
24072                     } catch (e) {
24073                         return;
24074                     }
24075                     Roo.TaskMgr.stop(task);
24076                     this.initEditor.defer(10, this);
24077                 }
24078             },
24079             interval : 10,
24080             duration:10000,
24081             scope: this
24082         };
24083         Roo.TaskMgr.start(task);
24084
24085         if(!this.width){
24086             this.setSize(this.el.getSize());
24087         }
24088     },
24089
24090     // private
24091     onResize : function(w, h){
24092         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24093         if(this.el && this.iframe){
24094             if(typeof w == 'number'){
24095                 var aw = w - this.wrap.getFrameWidth('lr');
24096                 this.el.setWidth(this.adjustWidth('textarea', aw));
24097                 this.iframe.style.width = aw + 'px';
24098             }
24099             if(typeof h == 'number'){
24100                 var tbh = 0;
24101                 for (var i =0; i < this.toolbars.length;i++) {
24102                     // fixme - ask toolbars for heights?
24103                     tbh += this.toolbars[i].tb.el.getHeight();
24104                 }
24105                 
24106                 
24107                 
24108                 
24109                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24110                 this.el.setHeight(this.adjustWidth('textarea', ah));
24111                 this.iframe.style.height = ah + 'px';
24112                 if(this.doc){
24113                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24114                 }
24115             }
24116         }
24117     },
24118
24119     /**
24120      * Toggles the editor between standard and source edit mode.
24121      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24122      */
24123     toggleSourceEdit : function(sourceEditMode){
24124         
24125         this.sourceEditMode = sourceEditMode === true;
24126         
24127         if(this.sourceEditMode){
24128           
24129             this.syncValue();
24130             this.iframe.className = 'x-hidden';
24131             this.el.removeClass('x-hidden');
24132             this.el.dom.removeAttribute('tabIndex');
24133             this.el.focus();
24134         }else{
24135              
24136             this.pushValue();
24137             this.iframe.className = '';
24138             this.el.addClass('x-hidden');
24139             this.el.dom.setAttribute('tabIndex', -1);
24140             this.deferFocus();
24141         }
24142         this.setSize(this.wrap.getSize());
24143         this.fireEvent('editmodechange', this, this.sourceEditMode);
24144     },
24145
24146     // private used internally
24147     createLink : function(){
24148         var url = prompt(this.createLinkText, this.defaultLinkValue);
24149         if(url && url != 'http:/'+'/'){
24150             this.relayCmd('createlink', url);
24151         }
24152     },
24153
24154     // private (for BoxComponent)
24155     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24156
24157     // private (for BoxComponent)
24158     getResizeEl : function(){
24159         return this.wrap;
24160     },
24161
24162     // private (for BoxComponent)
24163     getPositionEl : function(){
24164         return this.wrap;
24165     },
24166
24167     // private
24168     initEvents : function(){
24169         this.originalValue = this.getValue();
24170     },
24171
24172     /**
24173      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24174      * @method
24175      */
24176     markInvalid : Roo.emptyFn,
24177     /**
24178      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24179      * @method
24180      */
24181     clearInvalid : Roo.emptyFn,
24182
24183     setValue : function(v){
24184         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24185         this.pushValue();
24186     },
24187
24188     /**
24189      * Protected method that will not generally be called directly. If you need/want
24190      * custom HTML cleanup, this is the method you should override.
24191      * @param {String} html The HTML to be cleaned
24192      * return {String} The cleaned HTML
24193      */
24194     cleanHtml : function(html){
24195         html = String(html);
24196         if(html.length > 5){
24197             if(Roo.isSafari){ // strip safari nonsense
24198                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24199             }
24200         }
24201         if(html == '&nbsp;'){
24202             html = '';
24203         }
24204         return html;
24205     },
24206
24207     /**
24208      * Protected method that will not generally be called directly. Syncs the contents
24209      * of the editor iframe with the textarea.
24210      */
24211     syncValue : function(){
24212         if(this.initialized){
24213             var bd = (this.doc.body || this.doc.documentElement);
24214             var html = bd.innerHTML;
24215             if(Roo.isSafari){
24216                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24217                 var m = bs.match(/text-align:(.*?);/i);
24218                 if(m && m[1]){
24219                     html = '<div style="'+m[0]+'">' + html + '</div>';
24220                 }
24221             }
24222             html = this.cleanHtml(html);
24223             if(this.fireEvent('beforesync', this, html) !== false){
24224                 this.el.dom.value = html;
24225                 this.fireEvent('sync', this, html);
24226             }
24227         }
24228     },
24229
24230     /**
24231      * Protected method that will not generally be called directly. Pushes the value of the textarea
24232      * into the iframe editor.
24233      */
24234     pushValue : function(){
24235         if(this.initialized){
24236             var v = this.el.dom.value;
24237             if(v.length < 1){
24238                 v = '&#160;';
24239             }
24240             if(this.fireEvent('beforepush', this, v) !== false){
24241                 (this.doc.body || this.doc.documentElement).innerHTML = v;
24242                 this.fireEvent('push', this, v);
24243             }
24244         }
24245     },
24246
24247     // private
24248     deferFocus : function(){
24249         this.focus.defer(10, this);
24250     },
24251
24252     // doc'ed in Field
24253     focus : function(){
24254         if(this.win && !this.sourceEditMode){
24255             this.win.focus();
24256         }else{
24257             this.el.focus();
24258         }
24259     },
24260     
24261     assignDocWin: function()
24262     {
24263         var iframe = this.iframe;
24264         
24265          if(Roo.isIE){
24266             this.doc = iframe.contentWindow.document;
24267             this.win = iframe.contentWindow;
24268         } else {
24269             if (!Roo.get(this.frameId)) {
24270                 return;
24271             }
24272             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24273             this.win = Roo.get(this.frameId).dom.contentWindow;
24274         }
24275     },
24276     
24277     // private
24278     initEditor : function(){
24279         //console.log("INIT EDITOR");
24280         this.assignDocWin();
24281         
24282         
24283         
24284         this.doc.designMode="on";
24285         this.doc.open();
24286         this.doc.write(this.getDocMarkup());
24287         this.doc.close();
24288         
24289         var dbody = (this.doc.body || this.doc.documentElement);
24290         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24291         // this copies styles from the containing element into thsi one..
24292         // not sure why we need all of this..
24293         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24294         ss['background-attachment'] = 'fixed'; // w3c
24295         dbody.bgProperties = 'fixed'; // ie
24296         Roo.DomHelper.applyStyles(dbody, ss);
24297         Roo.EventManager.on(this.doc, {
24298             'mousedown': this.onEditorEvent,
24299             'dblclick': this.onEditorEvent,
24300             'click': this.onEditorEvent,
24301             'keyup': this.onEditorEvent,
24302             buffer:100,
24303             scope: this
24304         });
24305         if(Roo.isGecko){
24306             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24307         }
24308         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24309             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24310         }
24311         this.initialized = true;
24312
24313         this.fireEvent('initialize', this);
24314         this.pushValue();
24315     },
24316
24317     // private
24318     onDestroy : function(){
24319         
24320         
24321         
24322         if(this.rendered){
24323             
24324             for (var i =0; i < this.toolbars.length;i++) {
24325                 // fixme - ask toolbars for heights?
24326                 this.toolbars[i].onDestroy();
24327             }
24328             
24329             this.wrap.dom.innerHTML = '';
24330             this.wrap.remove();
24331         }
24332     },
24333
24334     // private
24335     onFirstFocus : function(){
24336         
24337         this.assignDocWin();
24338         
24339         
24340         this.activated = true;
24341         for (var i =0; i < this.toolbars.length;i++) {
24342             this.toolbars[i].onFirstFocus();
24343         }
24344        
24345         if(Roo.isGecko){ // prevent silly gecko errors
24346             this.win.focus();
24347             var s = this.win.getSelection();
24348             if(!s.focusNode || s.focusNode.nodeType != 3){
24349                 var r = s.getRangeAt(0);
24350                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24351                 r.collapse(true);
24352                 this.deferFocus();
24353             }
24354             try{
24355                 this.execCmd('useCSS', true);
24356                 this.execCmd('styleWithCSS', false);
24357             }catch(e){}
24358         }
24359         this.fireEvent('activate', this);
24360     },
24361
24362     // private
24363     adjustFont: function(btn){
24364         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24365         //if(Roo.isSafari){ // safari
24366         //    adjust *= 2;
24367        // }
24368         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24369         if(Roo.isSafari){ // safari
24370             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24371             v =  (v < 10) ? 10 : v;
24372             v =  (v > 48) ? 48 : v;
24373             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24374             
24375         }
24376         
24377         
24378         v = Math.max(1, v+adjust);
24379         
24380         this.execCmd('FontSize', v  );
24381     },
24382
24383     onEditorEvent : function(e){
24384         this.fireEvent('editorevent', this, e);
24385       //  this.updateToolbar();
24386         this.syncValue();
24387     },
24388
24389     insertTag : function(tg)
24390     {
24391         // could be a bit smarter... -> wrap the current selected tRoo..
24392         
24393         this.execCmd("formatblock",   tg);
24394         
24395     },
24396     
24397     insertText : function(txt)
24398     {
24399         
24400         
24401         range = this.createRange();
24402         range.deleteContents();
24403                //alert(Sender.getAttribute('label'));
24404                
24405         range.insertNode(this.doc.createTextNode(txt));
24406     } ,
24407     
24408     // private
24409     relayBtnCmd : function(btn){
24410         this.relayCmd(btn.cmd);
24411     },
24412
24413     /**
24414      * Executes a Midas editor command on the editor document and performs necessary focus and
24415      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24416      * @param {String} cmd The Midas command
24417      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24418      */
24419     relayCmd : function(cmd, value){
24420         this.win.focus();
24421         this.execCmd(cmd, value);
24422         this.fireEvent('editorevent', this);
24423         //this.updateToolbar();
24424         this.deferFocus();
24425     },
24426
24427     /**
24428      * Executes a Midas editor command directly on the editor document.
24429      * For visual commands, you should use {@link #relayCmd} instead.
24430      * <b>This should only be called after the editor is initialized.</b>
24431      * @param {String} cmd The Midas command
24432      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24433      */
24434     execCmd : function(cmd, value){
24435         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24436         this.syncValue();
24437     },
24438
24439    
24440     /**
24441      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24442      * to insert tRoo.
24443      * @param {String} text
24444      */
24445     insertAtCursor : function(text){
24446         if(!this.activated){
24447             return;
24448         }
24449         if(Roo.isIE){
24450             this.win.focus();
24451             var r = this.doc.selection.createRange();
24452             if(r){
24453                 r.collapse(true);
24454                 r.pasteHTML(text);
24455                 this.syncValue();
24456                 this.deferFocus();
24457             }
24458         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24459             this.win.focus();
24460             this.execCmd('InsertHTML', text);
24461             this.deferFocus();
24462         }
24463     },
24464  // private
24465     mozKeyPress : function(e){
24466         if(e.ctrlKey){
24467             var c = e.getCharCode(), cmd;
24468           
24469             if(c > 0){
24470                 c = String.fromCharCode(c).toLowerCase();
24471                 switch(c){
24472                     case 'b':
24473                         cmd = 'bold';
24474                     break;
24475                     case 'i':
24476                         cmd = 'italic';
24477                     break;
24478                     case 'u':
24479                         cmd = 'underline';
24480                     case 'v':
24481                         this.cleanUpPaste.defer(100, this);
24482                         return;
24483                     break;
24484                 }
24485                 if(cmd){
24486                     this.win.focus();
24487                     this.execCmd(cmd);
24488                     this.deferFocus();
24489                     e.preventDefault();
24490                 }
24491                 
24492             }
24493         }
24494     },
24495
24496     // private
24497     fixKeys : function(){ // load time branching for fastest keydown performance
24498         if(Roo.isIE){
24499             return function(e){
24500                 var k = e.getKey(), r;
24501                 if(k == e.TAB){
24502                     e.stopEvent();
24503                     r = this.doc.selection.createRange();
24504                     if(r){
24505                         r.collapse(true);
24506                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24507                         this.deferFocus();
24508                     }
24509                     return;
24510                 }
24511                 
24512                 if(k == e.ENTER){
24513                     r = this.doc.selection.createRange();
24514                     if(r){
24515                         var target = r.parentElement();
24516                         if(!target || target.tagName.toLowerCase() != 'li'){
24517                             e.stopEvent();
24518                             r.pasteHTML('<br />');
24519                             r.collapse(false);
24520                             r.select();
24521                         }
24522                     }
24523                 }
24524                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24525                     this.cleanUpPaste.defer(100, this);
24526                     return;
24527                 }
24528                 
24529                 
24530             };
24531         }else if(Roo.isOpera){
24532             return function(e){
24533                 var k = e.getKey();
24534                 if(k == e.TAB){
24535                     e.stopEvent();
24536                     this.win.focus();
24537                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24538                     this.deferFocus();
24539                 }
24540                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24541                     this.cleanUpPaste.defer(100, this);
24542                     return;
24543                 }
24544                 
24545             };
24546         }else if(Roo.isSafari){
24547             return function(e){
24548                 var k = e.getKey();
24549                 
24550                 if(k == e.TAB){
24551                     e.stopEvent();
24552                     this.execCmd('InsertText','\t');
24553                     this.deferFocus();
24554                     return;
24555                 }
24556                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24557                     this.cleanUpPaste.defer(100, this);
24558                     return;
24559                 }
24560                 
24561              };
24562         }
24563     }(),
24564     
24565     getAllAncestors: function()
24566     {
24567         var p = this.getSelectedNode();
24568         var a = [];
24569         if (!p) {
24570             a.push(p); // push blank onto stack..
24571             p = this.getParentElement();
24572         }
24573         
24574         
24575         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24576             a.push(p);
24577             p = p.parentNode;
24578         }
24579         a.push(this.doc.body);
24580         return a;
24581     },
24582     lastSel : false,
24583     lastSelNode : false,
24584     
24585     
24586     getSelection : function() 
24587     {
24588         this.assignDocWin();
24589         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24590     },
24591     
24592     getSelectedNode: function() 
24593     {
24594         // this may only work on Gecko!!!
24595         
24596         // should we cache this!!!!
24597         
24598         
24599         
24600          
24601         var range = this.createRange(this.getSelection());
24602         
24603         if (Roo.isIE) {
24604             var parent = range.parentElement();
24605             while (true) {
24606                 var testRange = range.duplicate();
24607                 testRange.moveToElementText(parent);
24608                 if (testRange.inRange(range)) {
24609                     break;
24610                 }
24611                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24612                     break;
24613                 }
24614                 parent = parent.parentElement;
24615             }
24616             return parent;
24617         }
24618         
24619         
24620         var ar = range.endContainer.childNodes;
24621         if (!ar.length) {
24622             ar = range.commonAncestorContainer.childNodes;
24623             //alert(ar.length);
24624         }
24625         var nodes = [];
24626         var other_nodes = [];
24627         var has_other_nodes = false;
24628         for (var i=0;i<ar.length;i++) {
24629             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24630                 continue;
24631             }
24632             // fullly contained node.
24633             
24634             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24635                 nodes.push(ar[i]);
24636                 continue;
24637             }
24638             
24639             // probably selected..
24640             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24641                 other_nodes.push(ar[i]);
24642                 continue;
24643             }
24644             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24645                 continue;
24646             }
24647             
24648             
24649             has_other_nodes = true;
24650         }
24651         if (!nodes.length && other_nodes.length) {
24652             nodes= other_nodes;
24653         }
24654         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24655             return false;
24656         }
24657         
24658         return nodes[0];
24659     },
24660     createRange: function(sel)
24661     {
24662         // this has strange effects when using with 
24663         // top toolbar - not sure if it's a great idea.
24664         //this.editor.contentWindow.focus();
24665         if (typeof sel != "undefined") {
24666             try {
24667                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24668             } catch(e) {
24669                 return this.doc.createRange();
24670             }
24671         } else {
24672             return this.doc.createRange();
24673         }
24674     },
24675     getParentElement: function()
24676     {
24677         
24678         this.assignDocWin();
24679         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24680         
24681         var range = this.createRange(sel);
24682          
24683         try {
24684             var p = range.commonAncestorContainer;
24685             while (p.nodeType == 3) { // text node
24686                 p = p.parentNode;
24687             }
24688             return p;
24689         } catch (e) {
24690             return null;
24691         }
24692     
24693     },
24694     
24695     
24696     
24697     // BC Hacks - cause I cant work out what i was trying to do..
24698     rangeIntersectsNode : function(range, node)
24699     {
24700         var nodeRange = node.ownerDocument.createRange();
24701         try {
24702             nodeRange.selectNode(node);
24703         }
24704         catch (e) {
24705             nodeRange.selectNodeContents(node);
24706         }
24707
24708         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24709                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24710     },
24711     rangeCompareNode : function(range, node) {
24712         var nodeRange = node.ownerDocument.createRange();
24713         try {
24714             nodeRange.selectNode(node);
24715         } catch (e) {
24716             nodeRange.selectNodeContents(node);
24717         }
24718         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24719         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24720
24721         if (nodeIsBefore && !nodeIsAfter)
24722             return 0;
24723         if (!nodeIsBefore && nodeIsAfter)
24724             return 1;
24725         if (nodeIsBefore && nodeIsAfter)
24726             return 2;
24727
24728         return 3;
24729     },
24730
24731     // private? - in a new class?
24732     cleanUpPaste :  function()
24733     {
24734         // cleans up the whole document..
24735       //  console.log('cleanuppaste');
24736         this.cleanUpChildren(this.doc.body)
24737         
24738         
24739     },
24740     cleanUpChildren : function (n)
24741     {
24742         if (!n.childNodes.length) {
24743             return;
24744         }
24745         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24746            this.cleanUpChild(n.childNodes[i]);
24747         }
24748     },
24749     
24750     
24751         
24752     
24753     cleanUpChild : function (node)
24754     {
24755         //console.log(node);
24756         if (node.nodeName == "#text") {
24757             // clean up silly Windows -- stuff?
24758             return; 
24759         }
24760         if (node.nodeName == "#comment") {
24761             node.parentNode.removeChild(node);
24762             // clean up silly Windows -- stuff?
24763             return; 
24764         }
24765         
24766         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24767             // remove node.
24768             node.parentNode.removeChild(node);
24769             return;
24770             
24771         }
24772         if (!node.attributes || !node.attributes.length) {
24773             this.cleanUpChildren(node);
24774             return;
24775         }
24776         
24777         function cleanAttr(n,v)
24778         {
24779             
24780             if (v.match(/^\./) || v.match(/^\//)) {
24781                 return;
24782             }
24783             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24784                 return;
24785             }
24786             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24787             node.removeAttribute(n);
24788             
24789         }
24790         
24791         function cleanStyle(n,v)
24792         {
24793             if (v.match(/expression/)) { //XSS?? should we even bother..
24794                 node.removeAttribute(n);
24795                 return;
24796             }
24797             
24798             
24799             var parts = v.split(/;/);
24800             Roo.each(parts, function(p) {
24801                 p = p.replace(/\s+/g,'');
24802                 if (!p.length) {
24803                     return;
24804                 }
24805                 var l = p.split(':').shift().replace(/\s+/g,'');
24806                 
24807                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24808                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24809                     node.removeAttribute(n);
24810                     return false;
24811                 }
24812             });
24813             
24814             
24815         }
24816         
24817         
24818         for (var i = node.attributes.length-1; i > -1 ; i--) {
24819             var a = node.attributes[i];
24820             //console.log(a);
24821             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24822                 node.removeAttribute(a.name);
24823                 return;
24824             }
24825             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24826                 cleanAttr(a.name,a.value); // fixme..
24827                 return;
24828             }
24829             if (a.name == 'style') {
24830                 cleanStyle(a.name,a.value);
24831             }
24832             /// clean up MS crap..
24833             if (a.name == 'class') {
24834                 if (a.value.match(/^Mso/)) {
24835                     node.className = '';
24836                 }
24837             }
24838             
24839             // style cleanup!?
24840             // class cleanup?
24841             
24842         }
24843         
24844         
24845         this.cleanUpChildren(node);
24846         
24847         
24848     }
24849     
24850     
24851     // hide stuff that is not compatible
24852     /**
24853      * @event blur
24854      * @hide
24855      */
24856     /**
24857      * @event change
24858      * @hide
24859      */
24860     /**
24861      * @event focus
24862      * @hide
24863      */
24864     /**
24865      * @event specialkey
24866      * @hide
24867      */
24868     /**
24869      * @cfg {String} fieldClass @hide
24870      */
24871     /**
24872      * @cfg {String} focusClass @hide
24873      */
24874     /**
24875      * @cfg {String} autoCreate @hide
24876      */
24877     /**
24878      * @cfg {String} inputType @hide
24879      */
24880     /**
24881      * @cfg {String} invalidClass @hide
24882      */
24883     /**
24884      * @cfg {String} invalidText @hide
24885      */
24886     /**
24887      * @cfg {String} msgFx @hide
24888      */
24889     /**
24890      * @cfg {String} validateOnBlur @hide
24891      */
24892 });
24893
24894 Roo.form.HtmlEditor.white = [
24895         'area', 'br', 'img', 'input', 'hr', 'wbr',
24896         
24897        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24898        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24899        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24900        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24901        'table',   'ul',         'xmp', 
24902        
24903        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24904       'thead',   'tr', 
24905      
24906       'dir', 'menu', 'ol', 'ul', 'dl',
24907        
24908       'embed',  'object'
24909 ];
24910
24911
24912 Roo.form.HtmlEditor.black = [
24913     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24914         'applet', // 
24915         'base',   'basefont', 'bgsound', 'blink',  'body', 
24916         'frame',  'frameset', 'head',    'html',   'ilayer', 
24917         'iframe', 'layer',  'link',     'meta',    'object',   
24918         'script', 'style' ,'title',  'xml' // clean later..
24919 ];
24920 Roo.form.HtmlEditor.clean = [
24921     'script', 'style', 'title', 'xml'
24922 ];
24923
24924 // attributes..
24925
24926 Roo.form.HtmlEditor.ablack = [
24927     'on'
24928 ];
24929     
24930 Roo.form.HtmlEditor.aclean = [ 
24931     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24932 ];
24933
24934 // protocols..
24935 Roo.form.HtmlEditor.pwhite= [
24936         'http',  'https',  'mailto'
24937 ];
24938
24939 Roo.form.HtmlEditor.cwhite= [
24940         'text-align',
24941         'font-size'
24942 ];
24943
24944 // <script type="text/javascript">
24945 /*
24946  * Based on
24947  * Ext JS Library 1.1.1
24948  * Copyright(c) 2006-2007, Ext JS, LLC.
24949  *  
24950  
24951  */
24952
24953 /**
24954  * @class Roo.form.HtmlEditorToolbar1
24955  * Basic Toolbar
24956  * 
24957  * Usage:
24958  *
24959  new Roo.form.HtmlEditor({
24960     ....
24961     toolbars : [
24962         new Roo.form.HtmlEditorToolbar1({
24963             disable : { fonts: 1 , format: 1, ..., ... , ...],
24964             btns : [ .... ]
24965         })
24966     }
24967      
24968  * 
24969  * @cfg {Object} disable List of elements to disable..
24970  * @cfg {Array} btns List of additional buttons.
24971  * 
24972  * 
24973  * NEEDS Extra CSS? 
24974  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24975  */
24976  
24977 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24978 {
24979     
24980     Roo.apply(this, config);
24981     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24982     // dont call parent... till later.
24983 }
24984
24985 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24986     
24987     tb: false,
24988     
24989     rendered: false,
24990     
24991     editor : false,
24992     /**
24993      * @cfg {Object} disable  List of toolbar elements to disable
24994          
24995      */
24996     disable : false,
24997       /**
24998      * @cfg {Array} fontFamilies An array of available font families
24999      */
25000     fontFamilies : [
25001         'Arial',
25002         'Courier New',
25003         'Tahoma',
25004         'Times New Roman',
25005         'Verdana'
25006     ],
25007     
25008     specialChars : [
25009            "&#169;",
25010           "&#174;",     
25011           "&#8482;",    
25012           "&#163;" ,    
25013          // "&#8212;",    
25014           "&#8230;",    
25015           "&#247;" ,    
25016         //  "&#225;" ,     ?? a acute?
25017            "&#8364;"    , //Euro
25018        //   "&#8220;"    ,
25019         //  "&#8221;"    ,
25020         //  "&#8226;"    ,
25021           "&#176;"  //   , // degrees
25022
25023          // "&#233;"     , // e ecute
25024          // "&#250;"     , // u ecute?
25025     ],
25026     inputElements : [ 
25027             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25028             "input:submit", "input:button", "select", "textarea", "label" ],
25029     formats : [
25030         ["p"] ,  
25031         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25032         ["pre"],[ "code"], 
25033         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25034     ],
25035      /**
25036      * @cfg {String} defaultFont default font to use.
25037      */
25038     defaultFont: 'tahoma',
25039    
25040     fontSelect : false,
25041     
25042     
25043     formatCombo : false,
25044     
25045     init : function(editor)
25046     {
25047         this.editor = editor;
25048         
25049         
25050         var fid = editor.frameId;
25051         var etb = this;
25052         function btn(id, toggle, handler){
25053             var xid = fid + '-'+ id ;
25054             return {
25055                 id : xid,
25056                 cmd : id,
25057                 cls : 'x-btn-icon x-edit-'+id,
25058                 enableToggle:toggle !== false,
25059                 scope: editor, // was editor...
25060                 handler:handler||editor.relayBtnCmd,
25061                 clickEvent:'mousedown',
25062                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25063                 tabIndex:-1
25064             };
25065         }
25066         
25067         
25068         
25069         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25070         this.tb = tb;
25071          // stop form submits
25072         tb.el.on('click', function(e){
25073             e.preventDefault(); // what does this do?
25074         });
25075
25076         if(!this.disable.font && !Roo.isSafari){
25077             /* why no safari for fonts
25078             editor.fontSelect = tb.el.createChild({
25079                 tag:'select',
25080                 tabIndex: -1,
25081                 cls:'x-font-select',
25082                 html: editor.createFontOptions()
25083             });
25084             editor.fontSelect.on('change', function(){
25085                 var font = editor.fontSelect.dom.value;
25086                 editor.relayCmd('fontname', font);
25087                 editor.deferFocus();
25088             }, editor);
25089             tb.add(
25090                 editor.fontSelect.dom,
25091                 '-'
25092             );
25093             */
25094         };
25095         if(!this.disable.formats){
25096             this.formatCombo = new Roo.form.ComboBox({
25097                 store: new Roo.data.SimpleStore({
25098                     id : 'tag',
25099                     fields: ['tag'],
25100                     data : this.formats // from states.js
25101                 }),
25102                 blockFocus : true,
25103                 //autoCreate : {tag: "div",  size: "20"},
25104                 displayField:'tag',
25105                 typeAhead: false,
25106                 mode: 'local',
25107                 editable : false,
25108                 triggerAction: 'all',
25109                 emptyText:'Add tag',
25110                 selectOnFocus:true,
25111                 width:135,
25112                 listeners : {
25113                     'select': function(c, r, i) {
25114                         editor.insertTag(r.get('tag'));
25115                         editor.focus();
25116                     }
25117                 }
25118
25119             });
25120             tb.addField(this.formatCombo);
25121             
25122         }
25123         
25124         if(!this.disable.format){
25125             tb.add(
25126                 btn('bold'),
25127                 btn('italic'),
25128                 btn('underline')
25129             );
25130         };
25131         if(!this.disable.fontSize){
25132             tb.add(
25133                 '-',
25134                 
25135                 
25136                 btn('increasefontsize', false, editor.adjustFont),
25137                 btn('decreasefontsize', false, editor.adjustFont)
25138             );
25139         };
25140         
25141         
25142         if(this.disable.colors){
25143             tb.add(
25144                 '-', {
25145                     id:editor.frameId +'-forecolor',
25146                     cls:'x-btn-icon x-edit-forecolor',
25147                     clickEvent:'mousedown',
25148                     tooltip: this.buttonTips['forecolor'] || undefined,
25149                     tabIndex:-1,
25150                     menu : new Roo.menu.ColorMenu({
25151                         allowReselect: true,
25152                         focus: Roo.emptyFn,
25153                         value:'000000',
25154                         plain:true,
25155                         selectHandler: function(cp, color){
25156                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25157                             editor.deferFocus();
25158                         },
25159                         scope: editor,
25160                         clickEvent:'mousedown'
25161                     })
25162                 }, {
25163                     id:editor.frameId +'backcolor',
25164                     cls:'x-btn-icon x-edit-backcolor',
25165                     clickEvent:'mousedown',
25166                     tooltip: this.buttonTips['backcolor'] || undefined,
25167                     tabIndex:-1,
25168                     menu : new Roo.menu.ColorMenu({
25169                         focus: Roo.emptyFn,
25170                         value:'FFFFFF',
25171                         plain:true,
25172                         allowReselect: true,
25173                         selectHandler: function(cp, color){
25174                             if(Roo.isGecko){
25175                                 editor.execCmd('useCSS', false);
25176                                 editor.execCmd('hilitecolor', color);
25177                                 editor.execCmd('useCSS', true);
25178                                 editor.deferFocus();
25179                             }else{
25180                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25181                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25182                                 editor.deferFocus();
25183                             }
25184                         },
25185                         scope:editor,
25186                         clickEvent:'mousedown'
25187                     })
25188                 }
25189             );
25190         };
25191         // now add all the items...
25192         
25193
25194         if(!this.disable.alignments){
25195             tb.add(
25196                 '-',
25197                 btn('justifyleft'),
25198                 btn('justifycenter'),
25199                 btn('justifyright')
25200             );
25201         };
25202
25203         //if(!Roo.isSafari){
25204             if(!this.disable.links){
25205                 tb.add(
25206                     '-',
25207                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25208                 );
25209             };
25210
25211             if(!this.disable.lists){
25212                 tb.add(
25213                     '-',
25214                     btn('insertorderedlist'),
25215                     btn('insertunorderedlist')
25216                 );
25217             }
25218             if(!this.disable.sourceEdit){
25219                 tb.add(
25220                     '-',
25221                     btn('sourceedit', true, function(btn){
25222                         this.toggleSourceEdit(btn.pressed);
25223                     })
25224                 );
25225             }
25226         //}
25227         
25228         var smenu = { };
25229         // special menu.. - needs to be tidied up..
25230         if (!this.disable.special) {
25231             smenu = {
25232                 text: "&#169;",
25233                 cls: 'x-edit-none',
25234                 menu : {
25235                     items : []
25236                    }
25237             };
25238             for (var i =0; i < this.specialChars.length; i++) {
25239                 smenu.menu.items.push({
25240                     
25241                     html: this.specialChars[i],
25242                     handler: function(a,b) {
25243                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25244                         
25245                     },
25246                     tabIndex:-1
25247                 });
25248             }
25249             
25250             
25251             tb.add(smenu);
25252             
25253             
25254         }
25255         if (this.btns) {
25256             for(var i =0; i< this.btns.length;i++) {
25257                 var b = this.btns[i];
25258                 b.cls =  'x-edit-none';
25259                 b.scope = editor;
25260                 tb.add(b);
25261             }
25262         
25263         }
25264         
25265         
25266         
25267         // disable everything...
25268         
25269         this.tb.items.each(function(item){
25270            if(item.id != editor.frameId+ '-sourceedit'){
25271                 item.disable();
25272             }
25273         });
25274         this.rendered = true;
25275         
25276         // the all the btns;
25277         editor.on('editorevent', this.updateToolbar, this);
25278         // other toolbars need to implement this..
25279         //editor.on('editmodechange', this.updateToolbar, this);
25280     },
25281     
25282     
25283     
25284     /**
25285      * Protected method that will not generally be called directly. It triggers
25286      * a toolbar update by reading the markup state of the current selection in the editor.
25287      */
25288     updateToolbar: function(){
25289
25290         if(!this.editor.activated){
25291             this.editor.onFirstFocus();
25292             return;
25293         }
25294
25295         var btns = this.tb.items.map, 
25296             doc = this.editor.doc,
25297             frameId = this.editor.frameId;
25298
25299         if(!this.disable.font && !Roo.isSafari){
25300             /*
25301             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25302             if(name != this.fontSelect.dom.value){
25303                 this.fontSelect.dom.value = name;
25304             }
25305             */
25306         }
25307         if(!this.disable.format){
25308             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25309             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25310             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25311         }
25312         if(!this.disable.alignments){
25313             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25314             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25315             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25316         }
25317         if(!Roo.isSafari && !this.disable.lists){
25318             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25319             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25320         }
25321         
25322         var ans = this.editor.getAllAncestors();
25323         if (this.formatCombo) {
25324             
25325             
25326             var store = this.formatCombo.store;
25327             this.formatCombo.setValue("");
25328             for (var i =0; i < ans.length;i++) {
25329                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25330                     // select it..
25331                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25332                     break;
25333                 }
25334             }
25335         }
25336         
25337         
25338         
25339         // hides menus... - so this cant be on a menu...
25340         Roo.menu.MenuMgr.hideAll();
25341
25342         //this.editorsyncValue();
25343     },
25344    
25345     
25346     createFontOptions : function(){
25347         var buf = [], fs = this.fontFamilies, ff, lc;
25348         for(var i = 0, len = fs.length; i< len; i++){
25349             ff = fs[i];
25350             lc = ff.toLowerCase();
25351             buf.push(
25352                 '<option value="',lc,'" style="font-family:',ff,';"',
25353                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25354                     ff,
25355                 '</option>'
25356             );
25357         }
25358         return buf.join('');
25359     },
25360     
25361     toggleSourceEdit : function(sourceEditMode){
25362         if(sourceEditMode === undefined){
25363             sourceEditMode = !this.sourceEditMode;
25364         }
25365         this.sourceEditMode = sourceEditMode === true;
25366         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25367         // just toggle the button?
25368         if(btn.pressed !== this.editor.sourceEditMode){
25369             btn.toggle(this.editor.sourceEditMode);
25370             return;
25371         }
25372         
25373         if(this.sourceEditMode){
25374             this.tb.items.each(function(item){
25375                 if(item.cmd != 'sourceedit'){
25376                     item.disable();
25377                 }
25378             });
25379           
25380         }else{
25381             if(this.initialized){
25382                 this.tb.items.each(function(item){
25383                     item.enable();
25384                 });
25385             }
25386             
25387         }
25388         // tell the editor that it's been pressed..
25389         this.editor.toggleSourceEdit(sourceEditMode);
25390        
25391     },
25392      /**
25393      * Object collection of toolbar tooltips for the buttons in the editor. The key
25394      * is the command id associated with that button and the value is a valid QuickTips object.
25395      * For example:
25396 <pre><code>
25397 {
25398     bold : {
25399         title: 'Bold (Ctrl+B)',
25400         text: 'Make the selected text bold.',
25401         cls: 'x-html-editor-tip'
25402     },
25403     italic : {
25404         title: 'Italic (Ctrl+I)',
25405         text: 'Make the selected text italic.',
25406         cls: 'x-html-editor-tip'
25407     },
25408     ...
25409 </code></pre>
25410     * @type Object
25411      */
25412     buttonTips : {
25413         bold : {
25414             title: 'Bold (Ctrl+B)',
25415             text: 'Make the selected text bold.',
25416             cls: 'x-html-editor-tip'
25417         },
25418         italic : {
25419             title: 'Italic (Ctrl+I)',
25420             text: 'Make the selected text italic.',
25421             cls: 'x-html-editor-tip'
25422         },
25423         underline : {
25424             title: 'Underline (Ctrl+U)',
25425             text: 'Underline the selected text.',
25426             cls: 'x-html-editor-tip'
25427         },
25428         increasefontsize : {
25429             title: 'Grow Text',
25430             text: 'Increase the font size.',
25431             cls: 'x-html-editor-tip'
25432         },
25433         decreasefontsize : {
25434             title: 'Shrink Text',
25435             text: 'Decrease the font size.',
25436             cls: 'x-html-editor-tip'
25437         },
25438         backcolor : {
25439             title: 'Text Highlight Color',
25440             text: 'Change the background color of the selected text.',
25441             cls: 'x-html-editor-tip'
25442         },
25443         forecolor : {
25444             title: 'Font Color',
25445             text: 'Change the color of the selected text.',
25446             cls: 'x-html-editor-tip'
25447         },
25448         justifyleft : {
25449             title: 'Align Text Left',
25450             text: 'Align text to the left.',
25451             cls: 'x-html-editor-tip'
25452         },
25453         justifycenter : {
25454             title: 'Center Text',
25455             text: 'Center text in the editor.',
25456             cls: 'x-html-editor-tip'
25457         },
25458         justifyright : {
25459             title: 'Align Text Right',
25460             text: 'Align text to the right.',
25461             cls: 'x-html-editor-tip'
25462         },
25463         insertunorderedlist : {
25464             title: 'Bullet List',
25465             text: 'Start a bulleted list.',
25466             cls: 'x-html-editor-tip'
25467         },
25468         insertorderedlist : {
25469             title: 'Numbered List',
25470             text: 'Start a numbered list.',
25471             cls: 'x-html-editor-tip'
25472         },
25473         createlink : {
25474             title: 'Hyperlink',
25475             text: 'Make the selected text a hyperlink.',
25476             cls: 'x-html-editor-tip'
25477         },
25478         sourceedit : {
25479             title: 'Source Edit',
25480             text: 'Switch to source editing mode.',
25481             cls: 'x-html-editor-tip'
25482         }
25483     },
25484     // private
25485     onDestroy : function(){
25486         if(this.rendered){
25487             
25488             this.tb.items.each(function(item){
25489                 if(item.menu){
25490                     item.menu.removeAll();
25491                     if(item.menu.el){
25492                         item.menu.el.destroy();
25493                     }
25494                 }
25495                 item.destroy();
25496             });
25497              
25498         }
25499     },
25500     onFirstFocus: function() {
25501         this.tb.items.each(function(item){
25502            item.enable();
25503         });
25504     }
25505 });
25506
25507
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 /**
25521  * @class Roo.form.HtmlEditor.ToolbarContext
25522  * Context Toolbar
25523  * 
25524  * Usage:
25525  *
25526  new Roo.form.HtmlEditor({
25527     ....
25528     toolbars : [
25529         new Roo.form.HtmlEditor.ToolbarStandard(),
25530         new Roo.form.HtmlEditor.ToolbarContext()
25531         })
25532     }
25533      
25534  * 
25535  * @config : {Object} disable List of elements to disable.. (not done yet.)
25536  * 
25537  * 
25538  */
25539
25540 Roo.form.HtmlEditor.ToolbarContext = function(config)
25541 {
25542     
25543     Roo.apply(this, config);
25544     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25545     // dont call parent... till later.
25546 }
25547 Roo.form.HtmlEditor.ToolbarContext.types = {
25548     'IMG' : {
25549         width : {
25550             title: "Width",
25551             width: 40
25552         },
25553         height:  {
25554             title: "Height",
25555             width: 40
25556         },
25557         align: {
25558             title: "Align",
25559             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25560             width : 80
25561             
25562         },
25563         border: {
25564             title: "Border",
25565             width: 40
25566         },
25567         alt: {
25568             title: "Alt",
25569             width: 120
25570         },
25571         src : {
25572             title: "Src",
25573             width: 220
25574         }
25575         
25576     },
25577     'A' : {
25578         name : {
25579             title: "Name",
25580             width: 50
25581         },
25582         href:  {
25583             title: "Href",
25584             width: 220
25585         } // border?
25586         
25587     },
25588     'TABLE' : {
25589         rows : {
25590             title: "Rows",
25591             width: 20
25592         },
25593         cols : {
25594             title: "Cols",
25595             width: 20
25596         },
25597         width : {
25598             title: "Width",
25599             width: 40
25600         },
25601         height : {
25602             title: "Height",
25603             width: 40
25604         },
25605         border : {
25606             title: "Border",
25607             width: 20
25608         }
25609     },
25610     'TD' : {
25611         width : {
25612             title: "Width",
25613             width: 40
25614         },
25615         height : {
25616             title: "Height",
25617             width: 40
25618         },   
25619         align: {
25620             title: "Align",
25621             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25622             width: 40
25623         },
25624         valign: {
25625             title: "Valign",
25626             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25627             width: 40
25628         },
25629         colspan: {
25630             title: "Colspan",
25631             width: 20
25632             
25633         }
25634     },
25635     'INPUT' : {
25636         name : {
25637             title: "name",
25638             width: 120
25639         },
25640         value : {
25641             title: "Value",
25642             width: 120
25643         },
25644         width : {
25645             title: "Width",
25646             width: 40
25647         }
25648     },
25649     'LABEL' : {
25650         'for' : {
25651             title: "For",
25652             width: 120
25653         }
25654     },
25655     'TEXTAREA' : {
25656           name : {
25657             title: "name",
25658             width: 120
25659         },
25660         rows : {
25661             title: "Rows",
25662             width: 20
25663         },
25664         cols : {
25665             title: "Cols",
25666             width: 20
25667         }
25668     },
25669     'SELECT' : {
25670         name : {
25671             title: "name",
25672             width: 120
25673         },
25674         selectoptions : {
25675             title: "Options",
25676             width: 200
25677         }
25678     },
25679     'BODY' : {
25680         title : {
25681             title: "title",
25682             width: 120,
25683             disabled : true
25684         }
25685     }
25686 };
25687
25688
25689
25690 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25691     
25692     tb: false,
25693     
25694     rendered: false,
25695     
25696     editor : false,
25697     /**
25698      * @cfg {Object} disable  List of toolbar elements to disable
25699          
25700      */
25701     disable : false,
25702     
25703     
25704     
25705     toolbars : false,
25706     
25707     init : function(editor)
25708     {
25709         this.editor = editor;
25710         
25711         
25712         var fid = editor.frameId;
25713         var etb = this;
25714         function btn(id, toggle, handler){
25715             var xid = fid + '-'+ id ;
25716             return {
25717                 id : xid,
25718                 cmd : id,
25719                 cls : 'x-btn-icon x-edit-'+id,
25720                 enableToggle:toggle !== false,
25721                 scope: editor, // was editor...
25722                 handler:handler||editor.relayBtnCmd,
25723                 clickEvent:'mousedown',
25724                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25725                 tabIndex:-1
25726             };
25727         }
25728         // create a new element.
25729         var wdiv = editor.wrap.createChild({
25730                 tag: 'div'
25731             }, editor.wrap.dom.firstChild.nextSibling, true);
25732         
25733         // can we do this more than once??
25734         
25735          // stop form submits
25736       
25737  
25738         // disable everything...
25739         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25740         this.toolbars = {};
25741            
25742         for (var i in  ty) {
25743           
25744             this.toolbars[i] = this.buildToolbar(ty[i],i);
25745         }
25746         this.tb = this.toolbars.BODY;
25747         this.tb.el.show();
25748         
25749          
25750         this.rendered = true;
25751         
25752         // the all the btns;
25753         editor.on('editorevent', this.updateToolbar, this);
25754         // other toolbars need to implement this..
25755         //editor.on('editmodechange', this.updateToolbar, this);
25756     },
25757     
25758     
25759     
25760     /**
25761      * Protected method that will not generally be called directly. It triggers
25762      * a toolbar update by reading the markup state of the current selection in the editor.
25763      */
25764     updateToolbar: function(){
25765
25766         if(!this.editor.activated){
25767             this.editor.onFirstFocus();
25768             return;
25769         }
25770
25771         
25772         var ans = this.editor.getAllAncestors();
25773         
25774         // pick
25775         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25776         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25777         sel = sel ? sel : this.editor.doc.body;
25778         sel = sel.tagName.length ? sel : this.editor.doc.body;
25779         var tn = sel.tagName.toUpperCase();
25780         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25781         tn = sel.tagName.toUpperCase();
25782         if (this.tb.name  == tn) {
25783             return; // no change
25784         }
25785         this.tb.el.hide();
25786         ///console.log("show: " + tn);
25787         this.tb =  this.toolbars[tn];
25788         this.tb.el.show();
25789         this.tb.fields.each(function(e) {
25790             e.setValue(sel.getAttribute(e.name));
25791         });
25792         this.tb.selectedNode = sel;
25793         
25794         
25795         Roo.menu.MenuMgr.hideAll();
25796
25797         //this.editorsyncValue();
25798     },
25799    
25800        
25801     // private
25802     onDestroy : function(){
25803         if(this.rendered){
25804             
25805             this.tb.items.each(function(item){
25806                 if(item.menu){
25807                     item.menu.removeAll();
25808                     if(item.menu.el){
25809                         item.menu.el.destroy();
25810                     }
25811                 }
25812                 item.destroy();
25813             });
25814              
25815         }
25816     },
25817     onFirstFocus: function() {
25818         // need to do this for all the toolbars..
25819         this.tb.items.each(function(item){
25820            item.enable();
25821         });
25822     },
25823     buildToolbar: function(tlist, nm)
25824     {
25825         var editor = this.editor;
25826          // create a new element.
25827         var wdiv = editor.wrap.createChild({
25828                 tag: 'div'
25829             }, editor.wrap.dom.firstChild.nextSibling, true);
25830         
25831        
25832         var tb = new Roo.Toolbar(wdiv);
25833         tb.add(nm+ ":&nbsp;");
25834         for (var i in tlist) {
25835             var item = tlist[i];
25836             tb.add(item.title + ":&nbsp;");
25837             if (item.opts) {
25838                 // fixme
25839                 
25840               
25841                 tb.addField( new Roo.form.ComboBox({
25842                     store: new Roo.data.SimpleStore({
25843                         id : 'val',
25844                         fields: ['val'],
25845                         data : item.opts // from states.js
25846                     }),
25847                     name : i,
25848                     displayField:'val',
25849                     typeAhead: false,
25850                     mode: 'local',
25851                     editable : false,
25852                     triggerAction: 'all',
25853                     emptyText:'Select',
25854                     selectOnFocus:true,
25855                     width: item.width ? item.width  : 130,
25856                     listeners : {
25857                         'select': function(c, r, i) {
25858                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25859                         }
25860                     }
25861
25862                 }));
25863                 continue;
25864                     
25865                 
25866                 
25867                 
25868                 
25869                 tb.addField( new Roo.form.TextField({
25870                     name: i,
25871                     width: 100,
25872                     //allowBlank:false,
25873                     value: ''
25874                 }));
25875                 continue;
25876             }
25877             tb.addField( new Roo.form.TextField({
25878                 name: i,
25879                 width: item.width,
25880                 //allowBlank:true,
25881                 value: '',
25882                 listeners: {
25883                     'change' : function(f, nv, ov) {
25884                         tb.selectedNode.setAttribute(f.name, nv);
25885                     }
25886                 }
25887             }));
25888              
25889         }
25890         tb.el.on('click', function(e){
25891             e.preventDefault(); // what does this do?
25892         });
25893         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25894         tb.el.hide();
25895         tb.name = nm;
25896         // dont need to disable them... as they will get hidden
25897         return tb;
25898          
25899         
25900     }
25901     
25902     
25903     
25904     
25905 });
25906
25907
25908
25909
25910
25911 /*
25912  * Based on:
25913  * Ext JS Library 1.1.1
25914  * Copyright(c) 2006-2007, Ext JS, LLC.
25915  *
25916  * Originally Released Under LGPL - original licence link has changed is not relivant.
25917  *
25918  * Fork - LGPL
25919  * <script type="text/javascript">
25920  */
25921  
25922 /**
25923  * @class Roo.form.BasicForm
25924  * @extends Roo.util.Observable
25925  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25926  * @constructor
25927  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25928  * @param {Object} config Configuration options
25929  */
25930 Roo.form.BasicForm = function(el, config){
25931     this.allItems = [];
25932     this.childForms = [];
25933     Roo.apply(this, config);
25934     /*
25935      * The Roo.form.Field items in this form.
25936      * @type MixedCollection
25937      */
25938      
25939      
25940     this.items = new Roo.util.MixedCollection(false, function(o){
25941         return o.id || (o.id = Roo.id());
25942     });
25943     this.addEvents({
25944         /**
25945          * @event beforeaction
25946          * Fires before any action is performed. Return false to cancel the action.
25947          * @param {Form} this
25948          * @param {Action} action The action to be performed
25949          */
25950         beforeaction: true,
25951         /**
25952          * @event actionfailed
25953          * Fires when an action fails.
25954          * @param {Form} this
25955          * @param {Action} action The action that failed
25956          */
25957         actionfailed : true,
25958         /**
25959          * @event actioncomplete
25960          * Fires when an action is completed.
25961          * @param {Form} this
25962          * @param {Action} action The action that completed
25963          */
25964         actioncomplete : true
25965     });
25966     if(el){
25967         this.initEl(el);
25968     }
25969     Roo.form.BasicForm.superclass.constructor.call(this);
25970 };
25971
25972 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
25973     /**
25974      * @cfg {String} method
25975      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
25976      */
25977     /**
25978      * @cfg {DataReader} reader
25979      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
25980      * This is optional as there is built-in support for processing JSON.
25981      */
25982     /**
25983      * @cfg {DataReader} errorReader
25984      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
25985      * This is completely optional as there is built-in support for processing JSON.
25986      */
25987     /**
25988      * @cfg {String} url
25989      * The URL to use for form actions if one isn't supplied in the action options.
25990      */
25991     /**
25992      * @cfg {Boolean} fileUpload
25993      * Set to true if this form is a file upload.
25994      */
25995      
25996     /**
25997      * @cfg {Object} baseParams
25998      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
25999      */
26000      /**
26001      
26002     /**
26003      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26004      */
26005     timeout: 30,
26006
26007     // private
26008     activeAction : null,
26009
26010     /**
26011      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26012      * or setValues() data instead of when the form was first created.
26013      */
26014     trackResetOnLoad : false,
26015     
26016     
26017     /**
26018      * childForms - used for multi-tab forms
26019      * @type {Array}
26020      */
26021     childForms : false,
26022     
26023     /**
26024      * allItems - full list of fields.
26025      * @type {Array}
26026      */
26027     allItems : false,
26028     
26029     /**
26030      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26031      * element by passing it or its id or mask the form itself by passing in true.
26032      * @type Mixed
26033      */
26034     waitMsgTarget : false,
26035
26036     // private
26037     initEl : function(el){
26038         this.el = Roo.get(el);
26039         this.id = this.el.id || Roo.id();
26040         this.el.on('submit', this.onSubmit, this);
26041         this.el.addClass('x-form');
26042     },
26043
26044     // private
26045     onSubmit : function(e){
26046         e.stopEvent();
26047     },
26048
26049     /**
26050      * Returns true if client-side validation on the form is successful.
26051      * @return Boolean
26052      */
26053     isValid : function(){
26054         var valid = true;
26055         this.items.each(function(f){
26056            if(!f.validate()){
26057                valid = false;
26058            }
26059         });
26060         return valid;
26061     },
26062
26063     /**
26064      * Returns true if any fields in this form have changed since their original load.
26065      * @return Boolean
26066      */
26067     isDirty : function(){
26068         var dirty = false;
26069         this.items.each(function(f){
26070            if(f.isDirty()){
26071                dirty = true;
26072                return false;
26073            }
26074         });
26075         return dirty;
26076     },
26077
26078     /**
26079      * Performs a predefined action (submit or load) or custom actions you define on this form.
26080      * @param {String} actionName The name of the action type
26081      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26082      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26083      * accept other config options):
26084      * <pre>
26085 Property          Type             Description
26086 ----------------  ---------------  ----------------------------------------------------------------------------------
26087 url               String           The url for the action (defaults to the form's url)
26088 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26089 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26090 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26091                                    validate the form on the client (defaults to false)
26092      * </pre>
26093      * @return {BasicForm} this
26094      */
26095     doAction : function(action, options){
26096         if(typeof action == 'string'){
26097             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26098         }
26099         if(this.fireEvent('beforeaction', this, action) !== false){
26100             this.beforeAction(action);
26101             action.run.defer(100, action);
26102         }
26103         return this;
26104     },
26105
26106     /**
26107      * Shortcut to do a submit action.
26108      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26109      * @return {BasicForm} this
26110      */
26111     submit : function(options){
26112         this.doAction('submit', options);
26113         return this;
26114     },
26115
26116     /**
26117      * Shortcut to do a load action.
26118      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26119      * @return {BasicForm} this
26120      */
26121     load : function(options){
26122         this.doAction('load', options);
26123         return this;
26124     },
26125
26126     /**
26127      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26128      * @param {Record} record The record to edit
26129      * @return {BasicForm} this
26130      */
26131     updateRecord : function(record){
26132         record.beginEdit();
26133         var fs = record.fields;
26134         fs.each(function(f){
26135             var field = this.findField(f.name);
26136             if(field){
26137                 record.set(f.name, field.getValue());
26138             }
26139         }, this);
26140         record.endEdit();
26141         return this;
26142     },
26143
26144     /**
26145      * Loads an Roo.data.Record into this form.
26146      * @param {Record} record The record to load
26147      * @return {BasicForm} this
26148      */
26149     loadRecord : function(record){
26150         this.setValues(record.data);
26151         return this;
26152     },
26153
26154     // private
26155     beforeAction : function(action){
26156         var o = action.options;
26157         
26158        
26159         if(this.waitMsgTarget === true){
26160             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
26161         }else if(this.waitMsgTarget){
26162             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26163             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
26164         }else {
26165             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
26166         }
26167          
26168     },
26169
26170     // private
26171     afterAction : function(action, success){
26172         this.activeAction = null;
26173         var o = action.options;
26174         
26175         if(this.waitMsgTarget === true){
26176             this.el.unmask();
26177         }else if(this.waitMsgTarget){
26178             this.waitMsgTarget.unmask();
26179         }else{
26180             Roo.MessageBox.updateProgress(1);
26181             Roo.MessageBox.hide();
26182         }
26183          
26184         if(success){
26185             if(o.reset){
26186                 this.reset();
26187             }
26188             Roo.callback(o.success, o.scope, [this, action]);
26189             this.fireEvent('actioncomplete', this, action);
26190             
26191         }else{
26192             Roo.callback(o.failure, o.scope, [this, action]);
26193             // show an error message if no failed handler is set..
26194             if (!this.hasListener('actionfailed')) {
26195                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
26196             }
26197             
26198             this.fireEvent('actionfailed', this, action);
26199         }
26200         
26201     },
26202
26203     /**
26204      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26205      * @param {String} id The value to search for
26206      * @return Field
26207      */
26208     findField : function(id){
26209         var field = this.items.get(id);
26210         if(!field){
26211             this.items.each(function(f){
26212                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26213                     field = f;
26214                     return false;
26215                 }
26216             });
26217         }
26218         return field || null;
26219     },
26220
26221     /**
26222      * Add a secondary form to this one, 
26223      * Used to provide tabbed forms. One form is primary, with hidden values 
26224      * which mirror the elements from the other forms.
26225      * 
26226      * @param {Roo.form.Form} form to add.
26227      * 
26228      */
26229     addForm : function(form)
26230     {
26231        
26232         if (this.childForms.indexOf(form) > -1) {
26233             // already added..
26234             return;
26235         }
26236         this.childForms.push(form);
26237         var n = '';
26238         Roo.each(form.allItems, function (fe) {
26239             
26240             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26241             if (this.findField(n)) { // already added..
26242                 return;
26243             }
26244             var add = new Roo.form.Hidden({
26245                 name : n
26246             });
26247             add.render(this.el);
26248             
26249             this.add( add );
26250         }, this);
26251         
26252     },
26253     /**
26254      * Mark fields in this form invalid in bulk.
26255      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26256      * @return {BasicForm} this
26257      */
26258     markInvalid : function(errors){
26259         if(errors instanceof Array){
26260             for(var i = 0, len = errors.length; i < len; i++){
26261                 var fieldError = errors[i];
26262                 var f = this.findField(fieldError.id);
26263                 if(f){
26264                     f.markInvalid(fieldError.msg);
26265                 }
26266             }
26267         }else{
26268             var field, id;
26269             for(id in errors){
26270                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26271                     field.markInvalid(errors[id]);
26272                 }
26273             }
26274         }
26275         Roo.each(this.childForms || [], function (f) {
26276             f.markInvalid(errors);
26277         });
26278         
26279         return this;
26280     },
26281
26282     /**
26283      * Set values for fields in this form in bulk.
26284      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26285      * @return {BasicForm} this
26286      */
26287     setValues : function(values){
26288         if(values instanceof Array){ // array of objects
26289             for(var i = 0, len = values.length; i < len; i++){
26290                 var v = values[i];
26291                 var f = this.findField(v.id);
26292                 if(f){
26293                     f.setValue(v.value);
26294                     if(this.trackResetOnLoad){
26295                         f.originalValue = f.getValue();
26296                     }
26297                 }
26298             }
26299         }else{ // object hash
26300             var field, id;
26301             for(id in values){
26302                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26303                     
26304                     if (field.setFromData && 
26305                         field.valueField && 
26306                         field.displayField &&
26307                         // combos' with local stores can 
26308                         // be queried via setValue()
26309                         // to set their value..
26310                         (field.store && !field.store.isLocal)
26311                         ) {
26312                         // it's a combo
26313                         var sd = { };
26314                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26315                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26316                         field.setFromData(sd);
26317                         
26318                     } else {
26319                         field.setValue(values[id]);
26320                     }
26321                     
26322                     
26323                     if(this.trackResetOnLoad){
26324                         field.originalValue = field.getValue();
26325                     }
26326                 }
26327             }
26328         }
26329          
26330         Roo.each(this.childForms || [], function (f) {
26331             f.setValues(values);
26332         });
26333                 
26334         return this;
26335     },
26336
26337     /**
26338      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26339      * they are returned as an array.
26340      * @param {Boolean} asString
26341      * @return {Object}
26342      */
26343     getValues : function(asString){
26344         if (this.childForms) {
26345             // copy values from the child forms
26346             Roo.each(this.childForms, function (f) {
26347                 this.setValues(f.getValues());
26348             }, this);
26349         }
26350         
26351         
26352         
26353         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26354         if(asString === true){
26355             return fs;
26356         }
26357         return Roo.urlDecode(fs);
26358     },
26359     
26360     /**
26361      * Returns the fields in this form as an object with key/value pairs. 
26362      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26363      * @return {Object}
26364      */
26365     getFieldValues : function()
26366     {
26367         if (this.childForms) {
26368             // copy values from the child forms
26369             Roo.each(this.childForms, function (f) {
26370                 this.setValues(f.getValues());
26371             }, this);
26372         }
26373         
26374         var ret = {};
26375         this.items.each(function(f){
26376             if (!f.getName()) {
26377                 return;
26378             }
26379             var v = f.getValue();
26380             if ((typeof(v) == 'object') && f.getRawValue) {
26381                 v = f.getRawValue() ; // dates..
26382             }
26383             ret[f.getName()] = v;
26384         });
26385         
26386         return ret;
26387     },
26388
26389     /**
26390      * Clears all invalid messages in this form.
26391      * @return {BasicForm} this
26392      */
26393     clearInvalid : function(){
26394         this.items.each(function(f){
26395            f.clearInvalid();
26396         });
26397         
26398         Roo.each(this.childForms || [], function (f) {
26399             f.clearInvalid();
26400         });
26401         
26402         
26403         return this;
26404     },
26405
26406     /**
26407      * Resets this form.
26408      * @return {BasicForm} this
26409      */
26410     reset : function(){
26411         this.items.each(function(f){
26412             f.reset();
26413         });
26414         
26415         Roo.each(this.childForms || [], function (f) {
26416             f.reset();
26417         });
26418        
26419         
26420         return this;
26421     },
26422
26423     /**
26424      * Add Roo.form components to this form.
26425      * @param {Field} field1
26426      * @param {Field} field2 (optional)
26427      * @param {Field} etc (optional)
26428      * @return {BasicForm} this
26429      */
26430     add : function(){
26431         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26432         return this;
26433     },
26434
26435
26436     /**
26437      * Removes a field from the items collection (does NOT remove its markup).
26438      * @param {Field} field
26439      * @return {BasicForm} this
26440      */
26441     remove : function(field){
26442         this.items.remove(field);
26443         return this;
26444     },
26445
26446     /**
26447      * Looks at the fields in this form, checks them for an id attribute,
26448      * and calls applyTo on the existing dom element with that id.
26449      * @return {BasicForm} this
26450      */
26451     render : function(){
26452         this.items.each(function(f){
26453             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26454                 f.applyTo(f.id);
26455             }
26456         });
26457         return this;
26458     },
26459
26460     /**
26461      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26462      * @param {Object} values
26463      * @return {BasicForm} this
26464      */
26465     applyToFields : function(o){
26466         this.items.each(function(f){
26467            Roo.apply(f, o);
26468         });
26469         return this;
26470     },
26471
26472     /**
26473      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26474      * @param {Object} values
26475      * @return {BasicForm} this
26476      */
26477     applyIfToFields : function(o){
26478         this.items.each(function(f){
26479            Roo.applyIf(f, o);
26480         });
26481         return this;
26482     }
26483 });
26484
26485 // back compat
26486 Roo.BasicForm = Roo.form.BasicForm;/*
26487  * Based on:
26488  * Ext JS Library 1.1.1
26489  * Copyright(c) 2006-2007, Ext JS, LLC.
26490  *
26491  * Originally Released Under LGPL - original licence link has changed is not relivant.
26492  *
26493  * Fork - LGPL
26494  * <script type="text/javascript">
26495  */
26496
26497 /**
26498  * @class Roo.form.Form
26499  * @extends Roo.form.BasicForm
26500  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26501  * @constructor
26502  * @param {Object} config Configuration options
26503  */
26504 Roo.form.Form = function(config){
26505     var xitems =  [];
26506     if (config.items) {
26507         xitems = config.items;
26508         delete config.items;
26509     }
26510    
26511     
26512     Roo.form.Form.superclass.constructor.call(this, null, config);
26513     this.url = this.url || this.action;
26514     if(!this.root){
26515         this.root = new Roo.form.Layout(Roo.applyIf({
26516             id: Roo.id()
26517         }, config));
26518     }
26519     this.active = this.root;
26520     /**
26521      * Array of all the buttons that have been added to this form via {@link addButton}
26522      * @type Array
26523      */
26524     this.buttons = [];
26525     this.allItems = [];
26526     this.addEvents({
26527         /**
26528          * @event clientvalidation
26529          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26530          * @param {Form} this
26531          * @param {Boolean} valid true if the form has passed client-side validation
26532          */
26533         clientvalidation: true,
26534         /**
26535          * @event rendered
26536          * Fires when the form is rendered
26537          * @param {Roo.form.Form} form
26538          */
26539         rendered : true
26540     });
26541     
26542     if (this.progressUrl) {
26543             // push a hidden field onto the list of fields..
26544             this.addxtype( {
26545                     xns: Roo.form, 
26546                     xtype : 'Hidden', 
26547                     name : 'UPLOAD_IDENTIFIER' 
26548             });
26549         }
26550         
26551     
26552     Roo.each(xitems, this.addxtype, this);
26553     
26554     
26555     
26556 };
26557
26558 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26559     /**
26560      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26561      */
26562     /**
26563      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26564      */
26565     /**
26566      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26567      */
26568     buttonAlign:'center',
26569
26570     /**
26571      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26572      */
26573     minButtonWidth:75,
26574
26575     /**
26576      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26577      * This property cascades to child containers if not set.
26578      */
26579     labelAlign:'left',
26580
26581     /**
26582      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26583      * fires a looping event with that state. This is required to bind buttons to the valid
26584      * state using the config value formBind:true on the button.
26585      */
26586     monitorValid : false,
26587
26588     /**
26589      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26590      */
26591     monitorPoll : 200,
26592     
26593     /**
26594      * @cfg {String} progressUrl - Url to return progress data 
26595      */
26596     
26597     progressUrl : false,
26598   
26599     /**
26600      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26601      * fields are added and the column is closed. If no fields are passed the column remains open
26602      * until end() is called.
26603      * @param {Object} config The config to pass to the column
26604      * @param {Field} field1 (optional)
26605      * @param {Field} field2 (optional)
26606      * @param {Field} etc (optional)
26607      * @return Column The column container object
26608      */
26609     column : function(c){
26610         var col = new Roo.form.Column(c);
26611         this.start(col);
26612         if(arguments.length > 1){ // duplicate code required because of Opera
26613             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26614             this.end();
26615         }
26616         return col;
26617     },
26618
26619     /**
26620      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26621      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26622      * until end() is called.
26623      * @param {Object} config The config to pass to the fieldset
26624      * @param {Field} field1 (optional)
26625      * @param {Field} field2 (optional)
26626      * @param {Field} etc (optional)
26627      * @return FieldSet The fieldset container object
26628      */
26629     fieldset : function(c){
26630         var fs = new Roo.form.FieldSet(c);
26631         this.start(fs);
26632         if(arguments.length > 1){ // duplicate code required because of Opera
26633             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26634             this.end();
26635         }
26636         return fs;
26637     },
26638
26639     /**
26640      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26641      * fields are added and the container is closed. If no fields are passed the container remains open
26642      * until end() is called.
26643      * @param {Object} config The config to pass to the Layout
26644      * @param {Field} field1 (optional)
26645      * @param {Field} field2 (optional)
26646      * @param {Field} etc (optional)
26647      * @return Layout The container object
26648      */
26649     container : function(c){
26650         var l = new Roo.form.Layout(c);
26651         this.start(l);
26652         if(arguments.length > 1){ // duplicate code required because of Opera
26653             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26654             this.end();
26655         }
26656         return l;
26657     },
26658
26659     /**
26660      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26661      * @param {Object} container A Roo.form.Layout or subclass of Layout
26662      * @return {Form} this
26663      */
26664     start : function(c){
26665         // cascade label info
26666         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26667         this.active.stack.push(c);
26668         c.ownerCt = this.active;
26669         this.active = c;
26670         return this;
26671     },
26672
26673     /**
26674      * Closes the current open container
26675      * @return {Form} this
26676      */
26677     end : function(){
26678         if(this.active == this.root){
26679             return this;
26680         }
26681         this.active = this.active.ownerCt;
26682         return this;
26683     },
26684
26685     /**
26686      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26687      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26688      * as the label of the field.
26689      * @param {Field} field1
26690      * @param {Field} field2 (optional)
26691      * @param {Field} etc. (optional)
26692      * @return {Form} this
26693      */
26694     add : function(){
26695         this.active.stack.push.apply(this.active.stack, arguments);
26696         this.allItems.push.apply(this.allItems,arguments);
26697         var r = [];
26698         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26699             if(a[i].isFormField){
26700                 r.push(a[i]);
26701             }
26702         }
26703         if(r.length > 0){
26704             Roo.form.Form.superclass.add.apply(this, r);
26705         }
26706         return this;
26707     },
26708     
26709
26710     
26711     
26712     
26713      /**
26714      * Find any element that has been added to a form, using it's ID or name
26715      * This can include framesets, columns etc. along with regular fields..
26716      * @param {String} id - id or name to find.
26717      
26718      * @return {Element} e - or false if nothing found.
26719      */
26720     findbyId : function(id)
26721     {
26722         var ret = false;
26723         if (!id) {
26724             return ret;
26725         }
26726         Ext.each(this.allItems, function(f){
26727             if (f.id == id || f.name == id ){
26728                 ret = f;
26729                 return false;
26730             }
26731         });
26732         return ret;
26733     },
26734
26735     
26736     
26737     /**
26738      * Render this form into the passed container. This should only be called once!
26739      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26740      * @return {Form} this
26741      */
26742     render : function(ct)
26743     {
26744         
26745         
26746         
26747         ct = Roo.get(ct);
26748         var o = this.autoCreate || {
26749             tag: 'form',
26750             method : this.method || 'POST',
26751             id : this.id || Roo.id()
26752         };
26753         this.initEl(ct.createChild(o));
26754
26755         this.root.render(this.el);
26756         
26757        
26758              
26759         this.items.each(function(f){
26760             f.render('x-form-el-'+f.id);
26761         });
26762
26763         if(this.buttons.length > 0){
26764             // tables are required to maintain order and for correct IE layout
26765             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26766                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26767                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26768             }}, null, true);
26769             var tr = tb.getElementsByTagName('tr')[0];
26770             for(var i = 0, len = this.buttons.length; i < len; i++) {
26771                 var b = this.buttons[i];
26772                 var td = document.createElement('td');
26773                 td.className = 'x-form-btn-td';
26774                 b.render(tr.appendChild(td));
26775             }
26776         }
26777         if(this.monitorValid){ // initialize after render
26778             this.startMonitoring();
26779         }
26780         this.fireEvent('rendered', this);
26781         return this;
26782     },
26783
26784     /**
26785      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26786      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26787      * object or a valid Roo.DomHelper element config
26788      * @param {Function} handler The function called when the button is clicked
26789      * @param {Object} scope (optional) The scope of the handler function
26790      * @return {Roo.Button}
26791      */
26792     addButton : function(config, handler, scope){
26793         var bc = {
26794             handler: handler,
26795             scope: scope,
26796             minWidth: this.minButtonWidth,
26797             hideParent:true
26798         };
26799         if(typeof config == "string"){
26800             bc.text = config;
26801         }else{
26802             Roo.apply(bc, config);
26803         }
26804         var btn = new Roo.Button(null, bc);
26805         this.buttons.push(btn);
26806         return btn;
26807     },
26808
26809      /**
26810      * Adds a series of form elements (using the xtype property as the factory method.
26811      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26812      * @param {Object} config 
26813      */
26814     
26815     addxtype : function()
26816     {
26817         var ar = Array.prototype.slice.call(arguments, 0);
26818         var ret = false;
26819         for(var i = 0; i < ar.length; i++) {
26820             if (!ar[i]) {
26821                 continue; // skip -- if this happends something invalid got sent, we 
26822                 // should ignore it, as basically that interface element will not show up
26823                 // and that should be pretty obvious!!
26824             }
26825             
26826             if (Roo.form[ar[i].xtype]) {
26827                 ar[i].form = this;
26828                 var fe = Roo.factory(ar[i], Roo.form);
26829                 if (!ret) {
26830                     ret = fe;
26831                 }
26832                 fe.form = this;
26833                 if (fe.store) {
26834                     fe.store.form = this;
26835                 }
26836                 if (fe.isLayout) {  
26837                          
26838                     this.start(fe);
26839                     this.allItems.push(fe);
26840                     if (fe.items && fe.addxtype) {
26841                         fe.addxtype.apply(fe, fe.items);
26842                         delete fe.items;
26843                     }
26844                      this.end();
26845                     continue;
26846                 }
26847                 
26848                 
26849                  
26850                 this.add(fe);
26851               //  console.log('adding ' + ar[i].xtype);
26852             }
26853             if (ar[i].xtype == 'Button') {  
26854                 //console.log('adding button');
26855                 //console.log(ar[i]);
26856                 this.addButton(ar[i]);
26857                 this.allItems.push(fe);
26858                 continue;
26859             }
26860             
26861             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26862                 alert('end is not supported on xtype any more, use items');
26863             //    this.end();
26864             //    //console.log('adding end');
26865             }
26866             
26867         }
26868         return ret;
26869     },
26870     
26871     /**
26872      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26873      * option "monitorValid"
26874      */
26875     startMonitoring : function(){
26876         if(!this.bound){
26877             this.bound = true;
26878             Roo.TaskMgr.start({
26879                 run : this.bindHandler,
26880                 interval : this.monitorPoll || 200,
26881                 scope: this
26882             });
26883         }
26884     },
26885
26886     /**
26887      * Stops monitoring of the valid state of this form
26888      */
26889     stopMonitoring : function(){
26890         this.bound = false;
26891     },
26892
26893     // private
26894     bindHandler : function(){
26895         if(!this.bound){
26896             return false; // stops binding
26897         }
26898         var valid = true;
26899         this.items.each(function(f){
26900             if(!f.isValid(true)){
26901                 valid = false;
26902                 return false;
26903             }
26904         });
26905         for(var i = 0, len = this.buttons.length; i < len; i++){
26906             var btn = this.buttons[i];
26907             if(btn.formBind === true && btn.disabled === valid){
26908                 btn.setDisabled(!valid);
26909             }
26910         }
26911         this.fireEvent('clientvalidation', this, valid);
26912     }
26913     
26914     
26915     
26916     
26917     
26918     
26919     
26920     
26921 });
26922
26923
26924 // back compat
26925 Roo.Form = Roo.form.Form;
26926 /*
26927  * Based on:
26928  * Ext JS Library 1.1.1
26929  * Copyright(c) 2006-2007, Ext JS, LLC.
26930  *
26931  * Originally Released Under LGPL - original licence link has changed is not relivant.
26932  *
26933  * Fork - LGPL
26934  * <script type="text/javascript">
26935  */
26936  
26937  /**
26938  * @class Roo.form.Action
26939  * Internal Class used to handle form actions
26940  * @constructor
26941  * @param {Roo.form.BasicForm} el The form element or its id
26942  * @param {Object} config Configuration options
26943  */
26944  
26945  
26946 // define the action interface
26947 Roo.form.Action = function(form, options){
26948     this.form = form;
26949     this.options = options || {};
26950 };
26951 /**
26952  * Client Validation Failed
26953  * @const 
26954  */
26955 Roo.form.Action.CLIENT_INVALID = 'client';
26956 /**
26957  * Server Validation Failed
26958  * @const 
26959  */
26960  Roo.form.Action.SERVER_INVALID = 'server';
26961  /**
26962  * Connect to Server Failed
26963  * @const 
26964  */
26965 Roo.form.Action.CONNECT_FAILURE = 'connect';
26966 /**
26967  * Reading Data from Server Failed
26968  * @const 
26969  */
26970 Roo.form.Action.LOAD_FAILURE = 'load';
26971
26972 Roo.form.Action.prototype = {
26973     type : 'default',
26974     failureType : undefined,
26975     response : undefined,
26976     result : undefined,
26977
26978     // interface method
26979     run : function(options){
26980
26981     },
26982
26983     // interface method
26984     success : function(response){
26985
26986     },
26987
26988     // interface method
26989     handleResponse : function(response){
26990
26991     },
26992
26993     // default connection failure
26994     failure : function(response){
26995         
26996         this.response = response;
26997         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26998         this.form.afterAction(this, false);
26999     },
27000
27001     processResponse : function(response){
27002         this.response = response;
27003         if(!response.responseText){
27004             return true;
27005         }
27006         this.result = this.handleResponse(response);
27007         return this.result;
27008     },
27009
27010     // utility functions used internally
27011     getUrl : function(appendParams){
27012         var url = this.options.url || this.form.url || this.form.el.dom.action;
27013         if(appendParams){
27014             var p = this.getParams();
27015             if(p){
27016                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27017             }
27018         }
27019         return url;
27020     },
27021
27022     getMethod : function(){
27023         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27024     },
27025
27026     getParams : function(){
27027         var bp = this.form.baseParams;
27028         var p = this.options.params;
27029         if(p){
27030             if(typeof p == "object"){
27031                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27032             }else if(typeof p == 'string' && bp){
27033                 p += '&' + Roo.urlEncode(bp);
27034             }
27035         }else if(bp){
27036             p = Roo.urlEncode(bp);
27037         }
27038         return p;
27039     },
27040
27041     createCallback : function(){
27042         return {
27043             success: this.success,
27044             failure: this.failure,
27045             scope: this,
27046             timeout: (this.form.timeout*1000),
27047             upload: this.form.fileUpload ? this.success : undefined
27048         };
27049     }
27050 };
27051
27052 Roo.form.Action.Submit = function(form, options){
27053     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27054 };
27055
27056 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27057     type : 'submit',
27058
27059     haveProgress : false,
27060     uploadComplete : false,
27061     
27062     // uploadProgress indicator.
27063     uploadProgress : function()
27064     {
27065         if (!this.form.progressUrl) {
27066             return;
27067         }
27068         
27069         if (!this.haveProgress) {
27070             Roo.MessageBox.progress("Uploading", "Uploading");
27071         }
27072         if (this.uploadComplete) {
27073            Roo.MessageBox.hide();
27074            return;
27075         }
27076         
27077         this.haveProgress = true;
27078    
27079         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27080         
27081         var c = new Roo.data.Connection();
27082         c.request({
27083             url : this.form.progressUrl,
27084             params: {
27085                 id : uid
27086             },
27087             method: 'GET',
27088             success : function(req){
27089                //console.log(data);
27090                 var rdata = false;
27091                 var edata;
27092                 try  {
27093                    rdata = Roo.decode(req.responseText)
27094                 } catch (e) {
27095                     Roo.log("Invalid data from server..");
27096                     Roo.log(edata);
27097                     return;
27098                 }
27099                 if (!rdata || !rdata.success) {
27100                     Roo.log(rdata);
27101                     return;
27102                 }
27103                 var data = rdata.data;
27104                 
27105                 if (this.uploadComplete) {
27106                    Roo.MessageBox.hide();
27107                    return;
27108                 }
27109                    
27110                 if (data){
27111                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27112                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27113                     );
27114                 }
27115                 this.uploadProgress.defer(2000,this);
27116             },
27117        
27118             failure: function(data) {
27119                 Roo.log('progress url failed ');
27120                 Roo.log(data);
27121             },
27122             scope : this
27123         });
27124            
27125     },
27126     
27127     
27128     run : function()
27129     {
27130         // run get Values on the form, so it syncs any secondary forms.
27131         this.form.getValues();
27132         
27133         var o = this.options;
27134         var method = this.getMethod();
27135         var isPost = method == 'POST';
27136         if(o.clientValidation === false || this.form.isValid()){
27137             
27138             if (this.form.progressUrl) {
27139                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27140                     (new Date() * 1) + '' + Math.random());
27141                     
27142             } 
27143             
27144             
27145             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27146                 form:this.form.el.dom,
27147                 url:this.getUrl(!isPost),
27148                 method: method,
27149                 params:isPost ? this.getParams() : null,
27150                 isUpload: this.form.fileUpload
27151             }));
27152             
27153             this.uploadProgress();
27154
27155         }else if (o.clientValidation !== false){ // client validation failed
27156             this.failureType = Roo.form.Action.CLIENT_INVALID;
27157             this.form.afterAction(this, false);
27158         }
27159     },
27160
27161     success : function(response)
27162     {
27163         this.uploadComplete= true;
27164         if (this.haveProgress) {
27165             Roo.MessageBox.hide();
27166         }
27167         
27168         
27169         var result = this.processResponse(response);
27170         if(result === true || result.success){
27171             this.form.afterAction(this, true);
27172             return;
27173         }
27174         if(result.errors){
27175             this.form.markInvalid(result.errors);
27176             this.failureType = Roo.form.Action.SERVER_INVALID;
27177         }
27178         this.form.afterAction(this, false);
27179     },
27180     failure : function(response)
27181     {
27182         this.uploadComplete= true;
27183         if (this.haveProgress) {
27184             Roo.MessageBox.hide();
27185         }
27186         
27187         
27188         this.response = response;
27189         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27190         this.form.afterAction(this, false);
27191     },
27192     
27193     handleResponse : function(response){
27194         if(this.form.errorReader){
27195             var rs = this.form.errorReader.read(response);
27196             var errors = [];
27197             if(rs.records){
27198                 for(var i = 0, len = rs.records.length; i < len; i++) {
27199                     var r = rs.records[i];
27200                     errors[i] = r.data;
27201                 }
27202             }
27203             if(errors.length < 1){
27204                 errors = null;
27205             }
27206             return {
27207                 success : rs.success,
27208                 errors : errors
27209             };
27210         }
27211         var ret = false;
27212         try {
27213             ret = Roo.decode(response.responseText);
27214         } catch (e) {
27215             ret = {
27216                 success: false,
27217                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27218                 errors : []
27219             };
27220         }
27221         return ret;
27222         
27223     }
27224 });
27225
27226
27227 Roo.form.Action.Load = function(form, options){
27228     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27229     this.reader = this.form.reader;
27230 };
27231
27232 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27233     type : 'load',
27234
27235     run : function(){
27236         
27237         Roo.Ajax.request(Roo.apply(
27238                 this.createCallback(), {
27239                     method:this.getMethod(),
27240                     url:this.getUrl(false),
27241                     params:this.getParams()
27242         }));
27243     },
27244
27245     success : function(response){
27246         
27247         var result = this.processResponse(response);
27248         if(result === true || !result.success || !result.data){
27249             this.failureType = Roo.form.Action.LOAD_FAILURE;
27250             this.form.afterAction(this, false);
27251             return;
27252         }
27253         this.form.clearInvalid();
27254         this.form.setValues(result.data);
27255         this.form.afterAction(this, true);
27256     },
27257
27258     handleResponse : function(response){
27259         if(this.form.reader){
27260             var rs = this.form.reader.read(response);
27261             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27262             return {
27263                 success : rs.success,
27264                 data : data
27265             };
27266         }
27267         return Roo.decode(response.responseText);
27268     }
27269 });
27270
27271 Roo.form.Action.ACTION_TYPES = {
27272     'load' : Roo.form.Action.Load,
27273     'submit' : Roo.form.Action.Submit
27274 };/*
27275  * Based on:
27276  * Ext JS Library 1.1.1
27277  * Copyright(c) 2006-2007, Ext JS, LLC.
27278  *
27279  * Originally Released Under LGPL - original licence link has changed is not relivant.
27280  *
27281  * Fork - LGPL
27282  * <script type="text/javascript">
27283  */
27284  
27285 /**
27286  * @class Roo.form.Layout
27287  * @extends Roo.Component
27288  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27289  * @constructor
27290  * @param {Object} config Configuration options
27291  */
27292 Roo.form.Layout = function(config){
27293     var xitems = [];
27294     if (config.items) {
27295         xitems = config.items;
27296         delete config.items;
27297     }
27298     Roo.form.Layout.superclass.constructor.call(this, config);
27299     this.stack = [];
27300     Roo.each(xitems, this.addxtype, this);
27301      
27302 };
27303
27304 Roo.extend(Roo.form.Layout, Roo.Component, {
27305     /**
27306      * @cfg {String/Object} autoCreate
27307      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27308      */
27309     /**
27310      * @cfg {String/Object/Function} style
27311      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27312      * a function which returns such a specification.
27313      */
27314     /**
27315      * @cfg {String} labelAlign
27316      * Valid values are "left," "top" and "right" (defaults to "left")
27317      */
27318     /**
27319      * @cfg {Number} labelWidth
27320      * Fixed width in pixels of all field labels (defaults to undefined)
27321      */
27322     /**
27323      * @cfg {Boolean} clear
27324      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27325      */
27326     clear : true,
27327     /**
27328      * @cfg {String} labelSeparator
27329      * The separator to use after field labels (defaults to ':')
27330      */
27331     labelSeparator : ':',
27332     /**
27333      * @cfg {Boolean} hideLabels
27334      * True to suppress the display of field labels in this layout (defaults to false)
27335      */
27336     hideLabels : false,
27337
27338     // private
27339     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27340     
27341     isLayout : true,
27342     
27343     // private
27344     onRender : function(ct, position){
27345         if(this.el){ // from markup
27346             this.el = Roo.get(this.el);
27347         }else {  // generate
27348             var cfg = this.getAutoCreate();
27349             this.el = ct.createChild(cfg, position);
27350         }
27351         if(this.style){
27352             this.el.applyStyles(this.style);
27353         }
27354         if(this.labelAlign){
27355             this.el.addClass('x-form-label-'+this.labelAlign);
27356         }
27357         if(this.hideLabels){
27358             this.labelStyle = "display:none";
27359             this.elementStyle = "padding-left:0;";
27360         }else{
27361             if(typeof this.labelWidth == 'number'){
27362                 this.labelStyle = "width:"+this.labelWidth+"px;";
27363                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27364             }
27365             if(this.labelAlign == 'top'){
27366                 this.labelStyle = "width:auto;";
27367                 this.elementStyle = "padding-left:0;";
27368             }
27369         }
27370         var stack = this.stack;
27371         var slen = stack.length;
27372         if(slen > 0){
27373             if(!this.fieldTpl){
27374                 var t = new Roo.Template(
27375                     '<div class="x-form-item {5}">',
27376                         '<label for="{0}" style="{2}">{1}{4}</label>',
27377                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27378                         '</div>',
27379                     '</div><div class="x-form-clear-left"></div>'
27380                 );
27381                 t.disableFormats = true;
27382                 t.compile();
27383                 Roo.form.Layout.prototype.fieldTpl = t;
27384             }
27385             for(var i = 0; i < slen; i++) {
27386                 if(stack[i].isFormField){
27387                     this.renderField(stack[i]);
27388                 }else{
27389                     this.renderComponent(stack[i]);
27390                 }
27391             }
27392         }
27393         if(this.clear){
27394             this.el.createChild({cls:'x-form-clear'});
27395         }
27396     },
27397
27398     // private
27399     renderField : function(f){
27400         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27401                f.id, //0
27402                f.fieldLabel, //1
27403                f.labelStyle||this.labelStyle||'', //2
27404                this.elementStyle||'', //3
27405                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27406                f.itemCls||this.itemCls||''  //5
27407        ], true).getPrevSibling());
27408     },
27409
27410     // private
27411     renderComponent : function(c){
27412         c.render(c.isLayout ? this.el : this.el.createChild());    
27413     },
27414     /**
27415      * Adds a object form elements (using the xtype property as the factory method.)
27416      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27417      * @param {Object} config 
27418      */
27419     addxtype : function(o)
27420     {
27421         // create the lement.
27422         o.form = this.form;
27423         var fe = Roo.factory(o, Roo.form);
27424         this.form.allItems.push(fe);
27425         this.stack.push(fe);
27426         
27427         if (fe.isFormField) {
27428             this.form.items.add(fe);
27429         }
27430          
27431         return fe;
27432     }
27433 });
27434
27435 /**
27436  * @class Roo.form.Column
27437  * @extends Roo.form.Layout
27438  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27439  * @constructor
27440  * @param {Object} config Configuration options
27441  */
27442 Roo.form.Column = function(config){
27443     Roo.form.Column.superclass.constructor.call(this, config);
27444 };
27445
27446 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27447     /**
27448      * @cfg {Number/String} width
27449      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27450      */
27451     /**
27452      * @cfg {String/Object} autoCreate
27453      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27454      */
27455
27456     // private
27457     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27458
27459     // private
27460     onRender : function(ct, position){
27461         Roo.form.Column.superclass.onRender.call(this, ct, position);
27462         if(this.width){
27463             this.el.setWidth(this.width);
27464         }
27465     }
27466 });
27467
27468
27469 /**
27470  * @class Roo.form.Row
27471  * @extends Roo.form.Layout
27472  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27473  * @constructor
27474  * @param {Object} config Configuration options
27475  */
27476
27477  
27478 Roo.form.Row = function(config){
27479     Roo.form.Row.superclass.constructor.call(this, config);
27480 };
27481  
27482 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27483       /**
27484      * @cfg {Number/String} width
27485      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27486      */
27487     /**
27488      * @cfg {Number/String} height
27489      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27490      */
27491     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27492     
27493     padWidth : 20,
27494     // private
27495     onRender : function(ct, position){
27496         //console.log('row render');
27497         if(!this.rowTpl){
27498             var t = new Roo.Template(
27499                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27500                     '<label for="{0}" style="{2}">{1}{4}</label>',
27501                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27502                     '</div>',
27503                 '</div>'
27504             );
27505             t.disableFormats = true;
27506             t.compile();
27507             Roo.form.Layout.prototype.rowTpl = t;
27508         }
27509         this.fieldTpl = this.rowTpl;
27510         
27511         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27512         var labelWidth = 100;
27513         
27514         if ((this.labelAlign != 'top')) {
27515             if (typeof this.labelWidth == 'number') {
27516                 labelWidth = this.labelWidth
27517             }
27518             this.padWidth =  20 + labelWidth;
27519             
27520         }
27521         
27522         Roo.form.Column.superclass.onRender.call(this, ct, position);
27523         if(this.width){
27524             this.el.setWidth(this.width);
27525         }
27526         if(this.height){
27527             this.el.setHeight(this.height);
27528         }
27529     },
27530     
27531     // private
27532     renderField : function(f){
27533         f.fieldEl = this.fieldTpl.append(this.el, [
27534                f.id, f.fieldLabel,
27535                f.labelStyle||this.labelStyle||'',
27536                this.elementStyle||'',
27537                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27538                f.itemCls||this.itemCls||'',
27539                f.width ? f.width + this.padWidth : 160 + this.padWidth
27540        ],true);
27541     }
27542 });
27543  
27544
27545 /**
27546  * @class Roo.form.FieldSet
27547  * @extends Roo.form.Layout
27548  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27549  * @constructor
27550  * @param {Object} config Configuration options
27551  */
27552 Roo.form.FieldSet = function(config){
27553     Roo.form.FieldSet.superclass.constructor.call(this, config);
27554 };
27555
27556 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27557     /**
27558      * @cfg {String} legend
27559      * The text to display as the legend for the FieldSet (defaults to '')
27560      */
27561     /**
27562      * @cfg {String/Object} autoCreate
27563      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27564      */
27565
27566     // private
27567     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27568
27569     // private
27570     onRender : function(ct, position){
27571         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27572         if(this.legend){
27573             this.setLegend(this.legend);
27574         }
27575     },
27576
27577     // private
27578     setLegend : function(text){
27579         if(this.rendered){
27580             this.el.child('legend').update(text);
27581         }
27582     }
27583 });/*
27584  * Based on:
27585  * Ext JS Library 1.1.1
27586  * Copyright(c) 2006-2007, Ext JS, LLC.
27587  *
27588  * Originally Released Under LGPL - original licence link has changed is not relivant.
27589  *
27590  * Fork - LGPL
27591  * <script type="text/javascript">
27592  */
27593 /**
27594  * @class Roo.form.VTypes
27595  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27596  * @singleton
27597  */
27598 Roo.form.VTypes = function(){
27599     // closure these in so they are only created once.
27600     var alpha = /^[a-zA-Z_]+$/;
27601     var alphanum = /^[a-zA-Z0-9_]+$/;
27602     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
27603     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27604
27605     // All these messages and functions are configurable
27606     return {
27607         /**
27608          * The function used to validate email addresses
27609          * @param {String} value The email address
27610          */
27611         'email' : function(v){
27612             return email.test(v);
27613         },
27614         /**
27615          * The error text to display when the email validation function returns false
27616          * @type String
27617          */
27618         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27619         /**
27620          * The keystroke filter mask to be applied on email input
27621          * @type RegExp
27622          */
27623         'emailMask' : /[a-z0-9_\.\-@]/i,
27624
27625         /**
27626          * The function used to validate URLs
27627          * @param {String} value The URL
27628          */
27629         'url' : function(v){
27630             return url.test(v);
27631         },
27632         /**
27633          * The error text to display when the url validation function returns false
27634          * @type String
27635          */
27636         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27637         
27638         /**
27639          * The function used to validate alpha values
27640          * @param {String} value The value
27641          */
27642         'alpha' : function(v){
27643             return alpha.test(v);
27644         },
27645         /**
27646          * The error text to display when the alpha validation function returns false
27647          * @type String
27648          */
27649         'alphaText' : 'This field should only contain letters and _',
27650         /**
27651          * The keystroke filter mask to be applied on alpha input
27652          * @type RegExp
27653          */
27654         'alphaMask' : /[a-z_]/i,
27655
27656         /**
27657          * The function used to validate alphanumeric values
27658          * @param {String} value The value
27659          */
27660         'alphanum' : function(v){
27661             return alphanum.test(v);
27662         },
27663         /**
27664          * The error text to display when the alphanumeric validation function returns false
27665          * @type String
27666          */
27667         'alphanumText' : 'This field should only contain letters, numbers and _',
27668         /**
27669          * The keystroke filter mask to be applied on alphanumeric input
27670          * @type RegExp
27671          */
27672         'alphanumMask' : /[a-z0-9_]/i
27673     };
27674 }();//<script type="text/javascript">
27675
27676 /**
27677  * @class Roo.form.FCKeditor
27678  * @extends Roo.form.TextArea
27679  * Wrapper around the FCKEditor http://www.fckeditor.net
27680  * @constructor
27681  * Creates a new FCKeditor
27682  * @param {Object} config Configuration options
27683  */
27684 Roo.form.FCKeditor = function(config){
27685     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27686     this.addEvents({
27687          /**
27688          * @event editorinit
27689          * Fired when the editor is initialized - you can add extra handlers here..
27690          * @param {FCKeditor} this
27691          * @param {Object} the FCK object.
27692          */
27693         editorinit : true
27694     });
27695     
27696     
27697 };
27698 Roo.form.FCKeditor.editors = { };
27699 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27700 {
27701     //defaultAutoCreate : {
27702     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27703     //},
27704     // private
27705     /**
27706      * @cfg {Object} fck options - see fck manual for details.
27707      */
27708     fckconfig : false,
27709     
27710     /**
27711      * @cfg {Object} fck toolbar set (Basic or Default)
27712      */
27713     toolbarSet : 'Basic',
27714     /**
27715      * @cfg {Object} fck BasePath
27716      */ 
27717     basePath : '/fckeditor/',
27718     
27719     
27720     frame : false,
27721     
27722     value : '',
27723     
27724    
27725     onRender : function(ct, position)
27726     {
27727         if(!this.el){
27728             this.defaultAutoCreate = {
27729                 tag: "textarea",
27730                 style:"width:300px;height:60px;",
27731                 autocomplete: "off"
27732             };
27733         }
27734         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27735         /*
27736         if(this.grow){
27737             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27738             if(this.preventScrollbars){
27739                 this.el.setStyle("overflow", "hidden");
27740             }
27741             this.el.setHeight(this.growMin);
27742         }
27743         */
27744         //console.log('onrender' + this.getId() );
27745         Roo.form.FCKeditor.editors[this.getId()] = this;
27746          
27747
27748         this.replaceTextarea() ;
27749         
27750     },
27751     
27752     getEditor : function() {
27753         return this.fckEditor;
27754     },
27755     /**
27756      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27757      * @param {Mixed} value The value to set
27758      */
27759     
27760     
27761     setValue : function(value)
27762     {
27763         //console.log('setValue: ' + value);
27764         
27765         if(typeof(value) == 'undefined') { // not sure why this is happending...
27766             return;
27767         }
27768         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27769         
27770         //if(!this.el || !this.getEditor()) {
27771         //    this.value = value;
27772             //this.setValue.defer(100,this,[value]);    
27773         //    return;
27774         //} 
27775         
27776         if(!this.getEditor()) {
27777             return;
27778         }
27779         
27780         this.getEditor().SetData(value);
27781         
27782         //
27783
27784     },
27785
27786     /**
27787      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27788      * @return {Mixed} value The field value
27789      */
27790     getValue : function()
27791     {
27792         
27793         if (this.frame && this.frame.dom.style.display == 'none') {
27794             return Roo.form.FCKeditor.superclass.getValue.call(this);
27795         }
27796         
27797         if(!this.el || !this.getEditor()) {
27798            
27799            // this.getValue.defer(100,this); 
27800             return this.value;
27801         }
27802        
27803         
27804         var value=this.getEditor().GetData();
27805         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27806         return Roo.form.FCKeditor.superclass.getValue.call(this);
27807         
27808
27809     },
27810
27811     /**
27812      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27813      * @return {Mixed} value The field value
27814      */
27815     getRawValue : function()
27816     {
27817         if (this.frame && this.frame.dom.style.display == 'none') {
27818             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27819         }
27820         
27821         if(!this.el || !this.getEditor()) {
27822             //this.getRawValue.defer(100,this); 
27823             return this.value;
27824             return;
27825         }
27826         
27827         
27828         
27829         var value=this.getEditor().GetData();
27830         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27831         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27832          
27833     },
27834     
27835     setSize : function(w,h) {
27836         
27837         
27838         
27839         //if (this.frame && this.frame.dom.style.display == 'none') {
27840         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27841         //    return;
27842         //}
27843         //if(!this.el || !this.getEditor()) {
27844         //    this.setSize.defer(100,this, [w,h]); 
27845         //    return;
27846         //}
27847         
27848         
27849         
27850         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27851         
27852         this.frame.dom.setAttribute('width', w);
27853         this.frame.dom.setAttribute('height', h);
27854         this.frame.setSize(w,h);
27855         
27856     },
27857     
27858     toggleSourceEdit : function(value) {
27859         
27860       
27861          
27862         this.el.dom.style.display = value ? '' : 'none';
27863         this.frame.dom.style.display = value ?  'none' : '';
27864         
27865     },
27866     
27867     
27868     focus: function(tag)
27869     {
27870         if (this.frame.dom.style.display == 'none') {
27871             return Roo.form.FCKeditor.superclass.focus.call(this);
27872         }
27873         if(!this.el || !this.getEditor()) {
27874             this.focus.defer(100,this, [tag]); 
27875             return;
27876         }
27877         
27878         
27879         
27880         
27881         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27882         this.getEditor().Focus();
27883         if (tgs.length) {
27884             if (!this.getEditor().Selection.GetSelection()) {
27885                 this.focus.defer(100,this, [tag]); 
27886                 return;
27887             }
27888             
27889             
27890             var r = this.getEditor().EditorDocument.createRange();
27891             r.setStart(tgs[0],0);
27892             r.setEnd(tgs[0],0);
27893             this.getEditor().Selection.GetSelection().removeAllRanges();
27894             this.getEditor().Selection.GetSelection().addRange(r);
27895             this.getEditor().Focus();
27896         }
27897         
27898     },
27899     
27900     
27901     
27902     replaceTextarea : function()
27903     {
27904         if ( document.getElementById( this.getId() + '___Frame' ) )
27905             return ;
27906         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27907         //{
27908             // We must check the elements firstly using the Id and then the name.
27909         var oTextarea = document.getElementById( this.getId() );
27910         
27911         var colElementsByName = document.getElementsByName( this.getId() ) ;
27912          
27913         oTextarea.style.display = 'none' ;
27914
27915         if ( oTextarea.tabIndex ) {            
27916             this.TabIndex = oTextarea.tabIndex ;
27917         }
27918         
27919         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27920         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27921         this.frame = Roo.get(this.getId() + '___Frame')
27922     },
27923     
27924     _getConfigHtml : function()
27925     {
27926         var sConfig = '' ;
27927
27928         for ( var o in this.fckconfig ) {
27929             sConfig += sConfig.length > 0  ? '&amp;' : '';
27930             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27931         }
27932
27933         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27934     },
27935     
27936     
27937     _getIFrameHtml : function()
27938     {
27939         var sFile = 'fckeditor.html' ;
27940         /* no idea what this is about..
27941         try
27942         {
27943             if ( (/fcksource=true/i).test( window.top.location.search ) )
27944                 sFile = 'fckeditor.original.html' ;
27945         }
27946         catch (e) { 
27947         */
27948
27949         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27950         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27951         
27952         
27953         var html = '<iframe id="' + this.getId() +
27954             '___Frame" src="' + sLink +
27955             '" width="' + this.width +
27956             '" height="' + this.height + '"' +
27957             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27958             ' frameborder="0" scrolling="no"></iframe>' ;
27959
27960         return html ;
27961     },
27962     
27963     _insertHtmlBefore : function( html, element )
27964     {
27965         if ( element.insertAdjacentHTML )       {
27966             // IE
27967             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27968         } else { // Gecko
27969             var oRange = document.createRange() ;
27970             oRange.setStartBefore( element ) ;
27971             var oFragment = oRange.createContextualFragment( html );
27972             element.parentNode.insertBefore( oFragment, element ) ;
27973         }
27974     }
27975     
27976     
27977   
27978     
27979     
27980     
27981     
27982
27983 });
27984
27985 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27986
27987 function FCKeditor_OnComplete(editorInstance){
27988     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27989     f.fckEditor = editorInstance;
27990     //console.log("loaded");
27991     f.fireEvent('editorinit', f, editorInstance);
27992
27993   
27994
27995  
27996
27997
27998
27999
28000
28001
28002
28003
28004
28005
28006
28007
28008
28009
28010
28011 //<script type="text/javascript">
28012 /**
28013  * @class Roo.form.GridField
28014  * @extends Roo.form.Field
28015  * Embed a grid (or editable grid into a form)
28016  * STATUS ALPHA
28017  * 
28018  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28019  * it needs 
28020  * xgrid.store = Roo.data.Store
28021  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28022  * xgrid.store.reader = Roo.data.JsonReader 
28023  * 
28024  * 
28025  * @constructor
28026  * Creates a new GridField
28027  * @param {Object} config Configuration options
28028  */
28029 Roo.form.GridField = function(config){
28030     Roo.form.GridField.superclass.constructor.call(this, config);
28031      
28032 };
28033
28034 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28035     /**
28036      * @cfg {Number} width  - used to restrict width of grid..
28037      */
28038     width : 100,
28039     /**
28040      * @cfg {Number} height - used to restrict height of grid..
28041      */
28042     height : 50,
28043      /**
28044      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28045          * 
28046          *}
28047      */
28048     xgrid : false, 
28049     /**
28050      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28051      * {tag: "input", type: "checkbox", autocomplete: "off"})
28052      */
28053    // defaultAutoCreate : { tag: 'div' },
28054     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28055     /**
28056      * @cfg {String} addTitle Text to include for adding a title.
28057      */
28058     addTitle : false,
28059     //
28060     onResize : function(){
28061         Roo.form.Field.superclass.onResize.apply(this, arguments);
28062     },
28063
28064     initEvents : function(){
28065         // Roo.form.Checkbox.superclass.initEvents.call(this);
28066         // has no events...
28067        
28068     },
28069
28070
28071     getResizeEl : function(){
28072         return this.wrap;
28073     },
28074
28075     getPositionEl : function(){
28076         return this.wrap;
28077     },
28078
28079     // private
28080     onRender : function(ct, position){
28081         
28082         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28083         var style = this.style;
28084         delete this.style;
28085         
28086         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28087         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28088         this.viewEl = this.wrap.createChild({ tag: 'div' });
28089         if (style) {
28090             this.viewEl.applyStyles(style);
28091         }
28092         if (this.width) {
28093             this.viewEl.setWidth(this.width);
28094         }
28095         if (this.height) {
28096             this.viewEl.setHeight(this.height);
28097         }
28098         //if(this.inputValue !== undefined){
28099         //this.setValue(this.value);
28100         
28101         
28102         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28103         
28104         
28105         this.grid.render();
28106         this.grid.getDataSource().on('remove', this.refreshValue, this);
28107         this.grid.getDataSource().on('update', this.refreshValue, this);
28108         this.grid.on('afteredit', this.refreshValue, this);
28109  
28110     },
28111      
28112     
28113     /**
28114      * Sets the value of the item. 
28115      * @param {String} either an object  or a string..
28116      */
28117     setValue : function(v){
28118         //this.value = v;
28119         v = v || []; // empty set..
28120         // this does not seem smart - it really only affects memoryproxy grids..
28121         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28122             var ds = this.grid.getDataSource();
28123             // assumes a json reader..
28124             var data = {}
28125             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28126             ds.loadData( data);
28127         }
28128         Roo.form.GridField.superclass.setValue.call(this, v);
28129         this.refreshValue();
28130         // should load data in the grid really....
28131     },
28132     
28133     // private
28134     refreshValue: function() {
28135          var val = [];
28136         this.grid.getDataSource().each(function(r) {
28137             val.push(r.data);
28138         });
28139         this.el.dom.value = Roo.encode(val);
28140     }
28141     
28142      
28143     
28144     
28145 });/*
28146  * Based on:
28147  * Ext JS Library 1.1.1
28148  * Copyright(c) 2006-2007, Ext JS, LLC.
28149  *
28150  * Originally Released Under LGPL - original licence link has changed is not relivant.
28151  *
28152  * Fork - LGPL
28153  * <script type="text/javascript">
28154  */
28155 /**
28156  * @class Roo.form.DisplayField
28157  * @extends Roo.form.Field
28158  * A generic Field to display non-editable data.
28159  * @constructor
28160  * Creates a new Display Field item.
28161  * @param {Object} config Configuration options
28162  */
28163 Roo.form.DisplayField = function(config){
28164     Roo.form.DisplayField.superclass.constructor.call(this, config);
28165     
28166 };
28167
28168 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28169     inputType:      'hidden',
28170     allowBlank:     true,
28171     readOnly:         true,
28172     
28173  
28174     /**
28175      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28176      */
28177     focusClass : undefined,
28178     /**
28179      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28180      */
28181     fieldClass: 'x-form-field',
28182     
28183      /**
28184      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28185      */
28186     valueRenderer: undefined,
28187     
28188     width: 100,
28189     /**
28190      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28191      * {tag: "input", type: "checkbox", autocomplete: "off"})
28192      */
28193      
28194  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28195
28196     onResize : function(){
28197         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28198         
28199     },
28200
28201     initEvents : function(){
28202         // Roo.form.Checkbox.superclass.initEvents.call(this);
28203         // has no events...
28204        
28205     },
28206
28207
28208     getResizeEl : function(){
28209         return this.wrap;
28210     },
28211
28212     getPositionEl : function(){
28213         return this.wrap;
28214     },
28215
28216     // private
28217     onRender : function(ct, position){
28218         
28219         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28220         //if(this.inputValue !== undefined){
28221         this.wrap = this.el.wrap();
28222         
28223         this.viewEl = this.wrap.createChild({ tag: 'div'});
28224         
28225         if (this.bodyStyle) {
28226             this.viewEl.applyStyles(this.bodyStyle);
28227         }
28228         //this.viewEl.setStyle('padding', '2px');
28229         
28230         this.setValue(this.value);
28231         
28232     },
28233 /*
28234     // private
28235     initValue : Roo.emptyFn,
28236
28237   */
28238
28239         // private
28240     onClick : function(){
28241         
28242     },
28243
28244     /**
28245      * Sets the checked state of the checkbox.
28246      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28247      */
28248     setValue : function(v){
28249         this.value = v;
28250         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28251         // this might be called before we have a dom element..
28252         if (!this.viewEl) {
28253             return;
28254         }
28255         this.viewEl.dom.innerHTML = html;
28256         Roo.form.DisplayField.superclass.setValue.call(this, v);
28257
28258     }
28259 });//<script type="text/javasscript">
28260  
28261
28262 /**
28263  * @class Roo.DDView
28264  * A DnD enabled version of Roo.View.
28265  * @param {Element/String} container The Element in which to create the View.
28266  * @param {String} tpl The template string used to create the markup for each element of the View
28267  * @param {Object} config The configuration properties. These include all the config options of
28268  * {@link Roo.View} plus some specific to this class.<br>
28269  * <p>
28270  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28271  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28272  * <p>
28273  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28274 .x-view-drag-insert-above {
28275         border-top:1px dotted #3366cc;
28276 }
28277 .x-view-drag-insert-below {
28278         border-bottom:1px dotted #3366cc;
28279 }
28280 </code></pre>
28281  * 
28282  */
28283  
28284 Roo.DDView = function(container, tpl, config) {
28285     Roo.DDView.superclass.constructor.apply(this, arguments);
28286     this.getEl().setStyle("outline", "0px none");
28287     this.getEl().unselectable();
28288     if (this.dragGroup) {
28289                 this.setDraggable(this.dragGroup.split(","));
28290     }
28291     if (this.dropGroup) {
28292                 this.setDroppable(this.dropGroup.split(","));
28293     }
28294     if (this.deletable) {
28295         this.setDeletable();
28296     }
28297     this.isDirtyFlag = false;
28298         this.addEvents({
28299                 "drop" : true
28300         });
28301 };
28302
28303 Roo.extend(Roo.DDView, Roo.View, {
28304 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28305 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28306 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28307 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28308
28309         isFormField: true,
28310
28311         reset: Roo.emptyFn,
28312         
28313         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28314
28315         validate: function() {
28316                 return true;
28317         },
28318         
28319         destroy: function() {
28320                 this.purgeListeners();
28321                 this.getEl.removeAllListeners();
28322                 this.getEl().remove();
28323                 if (this.dragZone) {
28324                         if (this.dragZone.destroy) {
28325                                 this.dragZone.destroy();
28326                         }
28327                 }
28328                 if (this.dropZone) {
28329                         if (this.dropZone.destroy) {
28330                                 this.dropZone.destroy();
28331                         }
28332                 }
28333         },
28334
28335 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28336         getName: function() {
28337                 return this.name;
28338         },
28339
28340 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28341         setValue: function(v) {
28342                 if (!this.store) {
28343                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28344                 }
28345                 var data = {};
28346                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28347                 this.store.proxy = new Roo.data.MemoryProxy(data);
28348                 this.store.load();
28349         },
28350
28351 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28352         getValue: function() {
28353                 var result = '(';
28354                 this.store.each(function(rec) {
28355                         result += rec.id + ',';
28356                 });
28357                 return result.substr(0, result.length - 1) + ')';
28358         },
28359         
28360         getIds: function() {
28361                 var i = 0, result = new Array(this.store.getCount());
28362                 this.store.each(function(rec) {
28363                         result[i++] = rec.id;
28364                 });
28365                 return result;
28366         },
28367         
28368         isDirty: function() {
28369                 return this.isDirtyFlag;
28370         },
28371
28372 /**
28373  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28374  *      whole Element becomes the target, and this causes the drop gesture to append.
28375  */
28376     getTargetFromEvent : function(e) {
28377                 var target = e.getTarget();
28378                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28379                 target = target.parentNode;
28380                 }
28381                 if (!target) {
28382                         target = this.el.dom.lastChild || this.el.dom;
28383                 }
28384                 return target;
28385     },
28386
28387 /**
28388  *      Create the drag data which consists of an object which has the property "ddel" as
28389  *      the drag proxy element. 
28390  */
28391     getDragData : function(e) {
28392         var target = this.findItemFromChild(e.getTarget());
28393                 if(target) {
28394                         this.handleSelection(e);
28395                         var selNodes = this.getSelectedNodes();
28396             var dragData = {
28397                 source: this,
28398                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28399                 nodes: selNodes,
28400                 records: []
28401                         };
28402                         var selectedIndices = this.getSelectedIndexes();
28403                         for (var i = 0; i < selectedIndices.length; i++) {
28404                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28405                         }
28406                         if (selNodes.length == 1) {
28407                                 dragData.ddel = target.cloneNode(true); // the div element
28408                         } else {
28409                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28410                                 div.className = 'multi-proxy';
28411                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28412                                         div.appendChild(selNodes[i].cloneNode(true));
28413                                 }
28414                                 dragData.ddel = div;
28415                         }
28416             //console.log(dragData)
28417             //console.log(dragData.ddel.innerHTML)
28418                         return dragData;
28419                 }
28420         //console.log('nodragData')
28421                 return false;
28422     },
28423     
28424 /**     Specify to which ddGroup items in this DDView may be dragged. */
28425     setDraggable: function(ddGroup) {
28426         if (ddGroup instanceof Array) {
28427                 Roo.each(ddGroup, this.setDraggable, this);
28428                 return;
28429         }
28430         if (this.dragZone) {
28431                 this.dragZone.addToGroup(ddGroup);
28432         } else {
28433                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28434                                 containerScroll: true,
28435                                 ddGroup: ddGroup 
28436
28437                         });
28438 //                      Draggability implies selection. DragZone's mousedown selects the element.
28439                         if (!this.multiSelect) { this.singleSelect = true; }
28440
28441 //                      Wire the DragZone's handlers up to methods in *this*
28442                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28443                 }
28444     },
28445
28446 /**     Specify from which ddGroup this DDView accepts drops. */
28447     setDroppable: function(ddGroup) {
28448         if (ddGroup instanceof Array) {
28449                 Roo.each(ddGroup, this.setDroppable, this);
28450                 return;
28451         }
28452         if (this.dropZone) {
28453                 this.dropZone.addToGroup(ddGroup);
28454         } else {
28455                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28456                                 containerScroll: true,
28457                                 ddGroup: ddGroup
28458                         });
28459
28460 //                      Wire the DropZone's handlers up to methods in *this*
28461                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28462                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28463                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28464                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28465                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28466                 }
28467     },
28468
28469 /**     Decide whether to drop above or below a View node. */
28470     getDropPoint : function(e, n, dd){
28471         if (n == this.el.dom) { return "above"; }
28472                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28473                 var c = t + (b - t) / 2;
28474                 var y = Roo.lib.Event.getPageY(e);
28475                 if(y <= c) {
28476                         return "above";
28477                 }else{
28478                         return "below";
28479                 }
28480     },
28481
28482     onNodeEnter : function(n, dd, e, data){
28483                 return false;
28484     },
28485     
28486     onNodeOver : function(n, dd, e, data){
28487                 var pt = this.getDropPoint(e, n, dd);
28488                 // set the insert point style on the target node
28489                 var dragElClass = this.dropNotAllowed;
28490                 if (pt) {
28491                         var targetElClass;
28492                         if (pt == "above"){
28493                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28494                                 targetElClass = "x-view-drag-insert-above";
28495                         } else {
28496                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28497                                 targetElClass = "x-view-drag-insert-below";
28498                         }
28499                         if (this.lastInsertClass != targetElClass){
28500                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28501                                 this.lastInsertClass = targetElClass;
28502                         }
28503                 }
28504                 return dragElClass;
28505         },
28506
28507     onNodeOut : function(n, dd, e, data){
28508                 this.removeDropIndicators(n);
28509     },
28510
28511     onNodeDrop : function(n, dd, e, data){
28512         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28513                 return false;
28514         }
28515         var pt = this.getDropPoint(e, n, dd);
28516                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28517                 if (pt == "below") { insertAt++; }
28518                 for (var i = 0; i < data.records.length; i++) {
28519                         var r = data.records[i];
28520                         var dup = this.store.getById(r.id);
28521                         if (dup && (dd != this.dragZone)) {
28522                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28523                         } else {
28524                                 if (data.copy) {
28525                                         this.store.insert(insertAt++, r.copy());
28526                                 } else {
28527                                         data.source.isDirtyFlag = true;
28528                                         r.store.remove(r);
28529                                         this.store.insert(insertAt++, r);
28530                                 }
28531                                 this.isDirtyFlag = true;
28532                         }
28533                 }
28534                 this.dragZone.cachedTarget = null;
28535                 return true;
28536     },
28537
28538     removeDropIndicators : function(n){
28539                 if(n){
28540                         Roo.fly(n).removeClass([
28541                                 "x-view-drag-insert-above",
28542                                 "x-view-drag-insert-below"]);
28543                         this.lastInsertClass = "_noclass";
28544                 }
28545     },
28546
28547 /**
28548  *      Utility method. Add a delete option to the DDView's context menu.
28549  *      @param {String} imageUrl The URL of the "delete" icon image.
28550  */
28551         setDeletable: function(imageUrl) {
28552                 if (!this.singleSelect && !this.multiSelect) {
28553                         this.singleSelect = true;
28554                 }
28555                 var c = this.getContextMenu();
28556                 this.contextMenu.on("itemclick", function(item) {
28557                         switch (item.id) {
28558                                 case "delete":
28559                                         this.remove(this.getSelectedIndexes());
28560                                         break;
28561                         }
28562                 }, this);
28563                 this.contextMenu.add({
28564                         icon: imageUrl,
28565                         id: "delete",
28566                         text: 'Delete'
28567                 });
28568         },
28569         
28570 /**     Return the context menu for this DDView. */
28571         getContextMenu: function() {
28572                 if (!this.contextMenu) {
28573 //                      Create the View's context menu
28574                         this.contextMenu = new Roo.menu.Menu({
28575                                 id: this.id + "-contextmenu"
28576                         });
28577                         this.el.on("contextmenu", this.showContextMenu, this);
28578                 }
28579                 return this.contextMenu;
28580         },
28581         
28582         disableContextMenu: function() {
28583                 if (this.contextMenu) {
28584                         this.el.un("contextmenu", this.showContextMenu, this);
28585                 }
28586         },
28587
28588         showContextMenu: function(e, item) {
28589         item = this.findItemFromChild(e.getTarget());
28590                 if (item) {
28591                         e.stopEvent();
28592                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28593                         this.contextMenu.showAt(e.getXY());
28594             }
28595     },
28596
28597 /**
28598  *      Remove {@link Roo.data.Record}s at the specified indices.
28599  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28600  */
28601     remove: function(selectedIndices) {
28602                 selectedIndices = [].concat(selectedIndices);
28603                 for (var i = 0; i < selectedIndices.length; i++) {
28604                         var rec = this.store.getAt(selectedIndices[i]);
28605                         this.store.remove(rec);
28606                 }
28607     },
28608
28609 /**
28610  *      Double click fires the event, but also, if this is draggable, and there is only one other
28611  *      related DropZone, it transfers the selected node.
28612  */
28613     onDblClick : function(e){
28614         var item = this.findItemFromChild(e.getTarget());
28615         if(item){
28616             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28617                 return false;
28618             }
28619             if (this.dragGroup) {
28620                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28621                     while (targets.indexOf(this.dropZone) > -1) {
28622                             targets.remove(this.dropZone);
28623                                 }
28624                     if (targets.length == 1) {
28625                                         this.dragZone.cachedTarget = null;
28626                         var el = Roo.get(targets[0].getEl());
28627                         var box = el.getBox(true);
28628                         targets[0].onNodeDrop(el.dom, {
28629                                 target: el.dom,
28630                                 xy: [box.x, box.y + box.height - 1]
28631                         }, null, this.getDragData(e));
28632                     }
28633                 }
28634         }
28635     },
28636     
28637     handleSelection: function(e) {
28638                 this.dragZone.cachedTarget = null;
28639         var item = this.findItemFromChild(e.getTarget());
28640         if (!item) {
28641                 this.clearSelections(true);
28642                 return;
28643         }
28644                 if (item && (this.multiSelect || this.singleSelect)){
28645                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28646                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28647                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28648                                 this.unselect(item);
28649                         } else {
28650                                 this.select(item, this.multiSelect && e.ctrlKey);
28651                                 this.lastSelection = item;
28652                         }
28653                 }
28654     },
28655
28656     onItemClick : function(item, index, e){
28657                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28658                         return false;
28659                 }
28660                 return true;
28661     },
28662
28663     unselect : function(nodeInfo, suppressEvent){
28664                 var node = this.getNode(nodeInfo);
28665                 if(node && this.isSelected(node)){
28666                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28667                                 Roo.fly(node).removeClass(this.selectedClass);
28668                                 this.selections.remove(node);
28669                                 if(!suppressEvent){
28670                                         this.fireEvent("selectionchange", this, this.selections);
28671                                 }
28672                         }
28673                 }
28674     }
28675 });
28676 /*
28677  * Based on:
28678  * Ext JS Library 1.1.1
28679  * Copyright(c) 2006-2007, Ext JS, LLC.
28680  *
28681  * Originally Released Under LGPL - original licence link has changed is not relivant.
28682  *
28683  * Fork - LGPL
28684  * <script type="text/javascript">
28685  */
28686  
28687 /**
28688  * @class Roo.LayoutManager
28689  * @extends Roo.util.Observable
28690  * Base class for layout managers.
28691  */
28692 Roo.LayoutManager = function(container, config){
28693     Roo.LayoutManager.superclass.constructor.call(this);
28694     this.el = Roo.get(container);
28695     // ie scrollbar fix
28696     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28697         document.body.scroll = "no";
28698     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28699         this.el.position('relative');
28700     }
28701     this.id = this.el.id;
28702     this.el.addClass("x-layout-container");
28703     /** false to disable window resize monitoring @type Boolean */
28704     this.monitorWindowResize = true;
28705     this.regions = {};
28706     this.addEvents({
28707         /**
28708          * @event layout
28709          * Fires when a layout is performed. 
28710          * @param {Roo.LayoutManager} this
28711          */
28712         "layout" : true,
28713         /**
28714          * @event regionresized
28715          * Fires when the user resizes a region. 
28716          * @param {Roo.LayoutRegion} region The resized region
28717          * @param {Number} newSize The new size (width for east/west, height for north/south)
28718          */
28719         "regionresized" : true,
28720         /**
28721          * @event regioncollapsed
28722          * Fires when a region is collapsed. 
28723          * @param {Roo.LayoutRegion} region The collapsed region
28724          */
28725         "regioncollapsed" : true,
28726         /**
28727          * @event regionexpanded
28728          * Fires when a region is expanded.  
28729          * @param {Roo.LayoutRegion} region The expanded region
28730          */
28731         "regionexpanded" : true
28732     });
28733     this.updating = false;
28734     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28735 };
28736
28737 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28738     /**
28739      * Returns true if this layout is currently being updated
28740      * @return {Boolean}
28741      */
28742     isUpdating : function(){
28743         return this.updating; 
28744     },
28745     
28746     /**
28747      * Suspend the LayoutManager from doing auto-layouts while
28748      * making multiple add or remove calls
28749      */
28750     beginUpdate : function(){
28751         this.updating = true;    
28752     },
28753     
28754     /**
28755      * Restore auto-layouts and optionally disable the manager from performing a layout
28756      * @param {Boolean} noLayout true to disable a layout update 
28757      */
28758     endUpdate : function(noLayout){
28759         this.updating = false;
28760         if(!noLayout){
28761             this.layout();
28762         }    
28763     },
28764     
28765     layout: function(){
28766         
28767     },
28768     
28769     onRegionResized : function(region, newSize){
28770         this.fireEvent("regionresized", region, newSize);
28771         this.layout();
28772     },
28773     
28774     onRegionCollapsed : function(region){
28775         this.fireEvent("regioncollapsed", region);
28776     },
28777     
28778     onRegionExpanded : function(region){
28779         this.fireEvent("regionexpanded", region);
28780     },
28781         
28782     /**
28783      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28784      * performs box-model adjustments.
28785      * @return {Object} The size as an object {width: (the width), height: (the height)}
28786      */
28787     getViewSize : function(){
28788         var size;
28789         if(this.el.dom != document.body){
28790             size = this.el.getSize();
28791         }else{
28792             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28793         }
28794         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28795         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28796         return size;
28797     },
28798     
28799     /**
28800      * Returns the Element this layout is bound to.
28801      * @return {Roo.Element}
28802      */
28803     getEl : function(){
28804         return this.el;
28805     },
28806     
28807     /**
28808      * Returns the specified region.
28809      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28810      * @return {Roo.LayoutRegion}
28811      */
28812     getRegion : function(target){
28813         return this.regions[target.toLowerCase()];
28814     },
28815     
28816     onWindowResize : function(){
28817         if(this.monitorWindowResize){
28818             this.layout();
28819         }
28820     }
28821 });/*
28822  * Based on:
28823  * Ext JS Library 1.1.1
28824  * Copyright(c) 2006-2007, Ext JS, LLC.
28825  *
28826  * Originally Released Under LGPL - original licence link has changed is not relivant.
28827  *
28828  * Fork - LGPL
28829  * <script type="text/javascript">
28830  */
28831 /**
28832  * @class Roo.BorderLayout
28833  * @extends Roo.LayoutManager
28834  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28835  * please see: <br><br>
28836  * <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>
28837  * <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>
28838  * Example:
28839  <pre><code>
28840  var layout = new Roo.BorderLayout(document.body, {
28841     north: {
28842         initialSize: 25,
28843         titlebar: false
28844     },
28845     west: {
28846         split:true,
28847         initialSize: 200,
28848         minSize: 175,
28849         maxSize: 400,
28850         titlebar: true,
28851         collapsible: true
28852     },
28853     east: {
28854         split:true,
28855         initialSize: 202,
28856         minSize: 175,
28857         maxSize: 400,
28858         titlebar: true,
28859         collapsible: true
28860     },
28861     south: {
28862         split:true,
28863         initialSize: 100,
28864         minSize: 100,
28865         maxSize: 200,
28866         titlebar: true,
28867         collapsible: true
28868     },
28869     center: {
28870         titlebar: true,
28871         autoScroll:true,
28872         resizeTabs: true,
28873         minTabWidth: 50,
28874         preferredTabWidth: 150
28875     }
28876 });
28877
28878 // shorthand
28879 var CP = Roo.ContentPanel;
28880
28881 layout.beginUpdate();
28882 layout.add("north", new CP("north", "North"));
28883 layout.add("south", new CP("south", {title: "South", closable: true}));
28884 layout.add("west", new CP("west", {title: "West"}));
28885 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28886 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28887 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28888 layout.getRegion("center").showPanel("center1");
28889 layout.endUpdate();
28890 </code></pre>
28891
28892 <b>The container the layout is rendered into can be either the body element or any other element.
28893 If it is not the body element, the container needs to either be an absolute positioned element,
28894 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28895 the container size if it is not the body element.</b>
28896
28897 * @constructor
28898 * Create a new BorderLayout
28899 * @param {String/HTMLElement/Element} container The container this layout is bound to
28900 * @param {Object} config Configuration options
28901  */
28902 Roo.BorderLayout = function(container, config){
28903     config = config || {};
28904     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28905     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28906     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28907         var target = this.factory.validRegions[i];
28908         if(config[target]){
28909             this.addRegion(target, config[target]);
28910         }
28911     }
28912 };
28913
28914 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28915     /**
28916      * Creates and adds a new region if it doesn't already exist.
28917      * @param {String} target The target region key (north, south, east, west or center).
28918      * @param {Object} config The regions config object
28919      * @return {BorderLayoutRegion} The new region
28920      */
28921     addRegion : function(target, config){
28922         if(!this.regions[target]){
28923             var r = this.factory.create(target, this, config);
28924             this.bindRegion(target, r);
28925         }
28926         return this.regions[target];
28927     },
28928
28929     // private (kinda)
28930     bindRegion : function(name, r){
28931         this.regions[name] = r;
28932         r.on("visibilitychange", this.layout, this);
28933         r.on("paneladded", this.layout, this);
28934         r.on("panelremoved", this.layout, this);
28935         r.on("invalidated", this.layout, this);
28936         r.on("resized", this.onRegionResized, this);
28937         r.on("collapsed", this.onRegionCollapsed, this);
28938         r.on("expanded", this.onRegionExpanded, this);
28939     },
28940
28941     /**
28942      * Performs a layout update.
28943      */
28944     layout : function(){
28945         if(this.updating) return;
28946         var size = this.getViewSize();
28947         var w = size.width;
28948         var h = size.height;
28949         var centerW = w;
28950         var centerH = h;
28951         var centerY = 0;
28952         var centerX = 0;
28953         //var x = 0, y = 0;
28954
28955         var rs = this.regions;
28956         var north = rs["north"];
28957         var south = rs["south"]; 
28958         var west = rs["west"];
28959         var east = rs["east"];
28960         var center = rs["center"];
28961         //if(this.hideOnLayout){ // not supported anymore
28962             //c.el.setStyle("display", "none");
28963         //}
28964         if(north && north.isVisible()){
28965             var b = north.getBox();
28966             var m = north.getMargins();
28967             b.width = w - (m.left+m.right);
28968             b.x = m.left;
28969             b.y = m.top;
28970             centerY = b.height + b.y + m.bottom;
28971             centerH -= centerY;
28972             north.updateBox(this.safeBox(b));
28973         }
28974         if(south && south.isVisible()){
28975             var b = south.getBox();
28976             var m = south.getMargins();
28977             b.width = w - (m.left+m.right);
28978             b.x = m.left;
28979             var totalHeight = (b.height + m.top + m.bottom);
28980             b.y = h - totalHeight + m.top;
28981             centerH -= totalHeight;
28982             south.updateBox(this.safeBox(b));
28983         }
28984         if(west && west.isVisible()){
28985             var b = west.getBox();
28986             var m = west.getMargins();
28987             b.height = centerH - (m.top+m.bottom);
28988             b.x = m.left;
28989             b.y = centerY + m.top;
28990             var totalWidth = (b.width + m.left + m.right);
28991             centerX += totalWidth;
28992             centerW -= totalWidth;
28993             west.updateBox(this.safeBox(b));
28994         }
28995         if(east && east.isVisible()){
28996             var b = east.getBox();
28997             var m = east.getMargins();
28998             b.height = centerH - (m.top+m.bottom);
28999             var totalWidth = (b.width + m.left + m.right);
29000             b.x = w - totalWidth + m.left;
29001             b.y = centerY + m.top;
29002             centerW -= totalWidth;
29003             east.updateBox(this.safeBox(b));
29004         }
29005         if(center){
29006             var m = center.getMargins();
29007             var centerBox = {
29008                 x: centerX + m.left,
29009                 y: centerY + m.top,
29010                 width: centerW - (m.left+m.right),
29011                 height: centerH - (m.top+m.bottom)
29012             };
29013             //if(this.hideOnLayout){
29014                 //center.el.setStyle("display", "block");
29015             //}
29016             center.updateBox(this.safeBox(centerBox));
29017         }
29018         this.el.repaint();
29019         this.fireEvent("layout", this);
29020     },
29021
29022     // private
29023     safeBox : function(box){
29024         box.width = Math.max(0, box.width);
29025         box.height = Math.max(0, box.height);
29026         return box;
29027     },
29028
29029     /**
29030      * Adds a ContentPanel (or subclass) to this layout.
29031      * @param {String} target The target region key (north, south, east, west or center).
29032      * @param {Roo.ContentPanel} panel The panel to add
29033      * @return {Roo.ContentPanel} The added panel
29034      */
29035     add : function(target, panel){
29036          
29037         target = target.toLowerCase();
29038         return this.regions[target].add(panel);
29039     },
29040
29041     /**
29042      * Remove a ContentPanel (or subclass) to this layout.
29043      * @param {String} target The target region key (north, south, east, west or center).
29044      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29045      * @return {Roo.ContentPanel} The removed panel
29046      */
29047     remove : function(target, panel){
29048         target = target.toLowerCase();
29049         return this.regions[target].remove(panel);
29050     },
29051
29052     /**
29053      * Searches all regions for a panel with the specified id
29054      * @param {String} panelId
29055      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29056      */
29057     findPanel : function(panelId){
29058         var rs = this.regions;
29059         for(var target in rs){
29060             if(typeof rs[target] != "function"){
29061                 var p = rs[target].getPanel(panelId);
29062                 if(p){
29063                     return p;
29064                 }
29065             }
29066         }
29067         return null;
29068     },
29069
29070     /**
29071      * Searches all regions for a panel with the specified id and activates (shows) it.
29072      * @param {String/ContentPanel} panelId The panels id or the panel itself
29073      * @return {Roo.ContentPanel} The shown panel or null
29074      */
29075     showPanel : function(panelId) {
29076       var rs = this.regions;
29077       for(var target in rs){
29078          var r = rs[target];
29079          if(typeof r != "function"){
29080             if(r.hasPanel(panelId)){
29081                return r.showPanel(panelId);
29082             }
29083          }
29084       }
29085       return null;
29086    },
29087
29088    /**
29089      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29090      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29091      */
29092     restoreState : function(provider){
29093         if(!provider){
29094             provider = Roo.state.Manager;
29095         }
29096         var sm = new Roo.LayoutStateManager();
29097         sm.init(this, provider);
29098     },
29099
29100     /**
29101      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29102      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29103      * a valid ContentPanel config object.  Example:
29104      * <pre><code>
29105 // Create the main layout
29106 var layout = new Roo.BorderLayout('main-ct', {
29107     west: {
29108         split:true,
29109         minSize: 175,
29110         titlebar: true
29111     },
29112     center: {
29113         title:'Components'
29114     }
29115 }, 'main-ct');
29116
29117 // Create and add multiple ContentPanels at once via configs
29118 layout.batchAdd({
29119    west: {
29120        id: 'source-files',
29121        autoCreate:true,
29122        title:'Ext Source Files',
29123        autoScroll:true,
29124        fitToFrame:true
29125    },
29126    center : {
29127        el: cview,
29128        autoScroll:true,
29129        fitToFrame:true,
29130        toolbar: tb,
29131        resizeEl:'cbody'
29132    }
29133 });
29134 </code></pre>
29135      * @param {Object} regions An object containing ContentPanel configs by region name
29136      */
29137     batchAdd : function(regions){
29138         this.beginUpdate();
29139         for(var rname in regions){
29140             var lr = this.regions[rname];
29141             if(lr){
29142                 this.addTypedPanels(lr, regions[rname]);
29143             }
29144         }
29145         this.endUpdate();
29146     },
29147
29148     // private
29149     addTypedPanels : function(lr, ps){
29150         if(typeof ps == 'string'){
29151             lr.add(new Roo.ContentPanel(ps));
29152         }
29153         else if(ps instanceof Array){
29154             for(var i =0, len = ps.length; i < len; i++){
29155                 this.addTypedPanels(lr, ps[i]);
29156             }
29157         }
29158         else if(!ps.events){ // raw config?
29159             var el = ps.el;
29160             delete ps.el; // prevent conflict
29161             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29162         }
29163         else {  // panel object assumed!
29164             lr.add(ps);
29165         }
29166     },
29167     /**
29168      * Adds a xtype elements to the layout.
29169      * <pre><code>
29170
29171 layout.addxtype({
29172        xtype : 'ContentPanel',
29173        region: 'west',
29174        items: [ .... ]
29175    }
29176 );
29177
29178 layout.addxtype({
29179         xtype : 'NestedLayoutPanel',
29180         region: 'west',
29181         layout: {
29182            center: { },
29183            west: { }   
29184         },
29185         items : [ ... list of content panels or nested layout panels.. ]
29186    }
29187 );
29188 </code></pre>
29189      * @param {Object} cfg Xtype definition of item to add.
29190      */
29191     addxtype : function(cfg)
29192     {
29193         // basically accepts a pannel...
29194         // can accept a layout region..!?!?
29195        // console.log('BorderLayout add ' + cfg.xtype)
29196         
29197         if (!cfg.xtype.match(/Panel$/)) {
29198             return false;
29199         }
29200         var ret = false;
29201         var region = cfg.region;
29202         delete cfg.region;
29203         
29204           
29205         var xitems = [];
29206         if (cfg.items) {
29207             xitems = cfg.items;
29208             delete cfg.items;
29209         }
29210         
29211         
29212         switch(cfg.xtype) 
29213         {
29214             case 'ContentPanel':  // ContentPanel (el, cfg)
29215             case 'ScrollPanel':  // ContentPanel (el, cfg)
29216                 if(cfg.autoCreate) {
29217                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29218                 } else {
29219                     var el = this.el.createChild();
29220                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29221                 }
29222                 
29223                 this.add(region, ret);
29224                 break;
29225             
29226             
29227             case 'TreePanel': // our new panel!
29228                 cfg.el = this.el.createChild();
29229                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29230                 this.add(region, ret);
29231                 break;
29232             
29233             case 'NestedLayoutPanel': 
29234                 // create a new Layout (which is  a Border Layout...
29235                 var el = this.el.createChild();
29236                 var clayout = cfg.layout;
29237                 delete cfg.layout;
29238                 clayout.items   = clayout.items  || [];
29239                 // replace this exitems with the clayout ones..
29240                 xitems = clayout.items;
29241                  
29242                 
29243                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29244                     cfg.background = false;
29245                 }
29246                 var layout = new Roo.BorderLayout(el, clayout);
29247                 
29248                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29249                 //console.log('adding nested layout panel '  + cfg.toSource());
29250                 this.add(region, ret);
29251                 
29252                 break;
29253                 
29254             case 'GridPanel': 
29255             
29256                 // needs grid and region
29257                 
29258                 //var el = this.getRegion(region).el.createChild();
29259                 var el = this.el.createChild();
29260                 // create the grid first...
29261                 
29262                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29263                 delete cfg.grid;
29264                 if (region == 'center' && this.active ) {
29265                     cfg.background = false;
29266                 }
29267                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29268                 
29269                 this.add(region, ret);
29270                 if (cfg.background) {
29271                     ret.on('activate', function(gp) {
29272                         if (!gp.grid.rendered) {
29273                             gp.grid.render();
29274                         }
29275                     });
29276                 } else {
29277                     grid.render();
29278                 }
29279                 break;
29280            
29281                
29282                 
29283                 
29284             default: 
29285                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29286                 return;
29287              // GridPanel (grid, cfg)
29288             
29289         }
29290         this.beginUpdate();
29291         // add children..
29292         Roo.each(xitems, function(i)  {
29293             ret.addxtype(i);
29294         });
29295         this.endUpdate();
29296         return ret;
29297         
29298     }
29299 });
29300
29301 /**
29302  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29303  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29304  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29305  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29306  * <pre><code>
29307 // shorthand
29308 var CP = Roo.ContentPanel;
29309
29310 var layout = Roo.BorderLayout.create({
29311     north: {
29312         initialSize: 25,
29313         titlebar: false,
29314         panels: [new CP("north", "North")]
29315     },
29316     west: {
29317         split:true,
29318         initialSize: 200,
29319         minSize: 175,
29320         maxSize: 400,
29321         titlebar: true,
29322         collapsible: true,
29323         panels: [new CP("west", {title: "West"})]
29324     },
29325     east: {
29326         split:true,
29327         initialSize: 202,
29328         minSize: 175,
29329         maxSize: 400,
29330         titlebar: true,
29331         collapsible: true,
29332         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29333     },
29334     south: {
29335         split:true,
29336         initialSize: 100,
29337         minSize: 100,
29338         maxSize: 200,
29339         titlebar: true,
29340         collapsible: true,
29341         panels: [new CP("south", {title: "South", closable: true})]
29342     },
29343     center: {
29344         titlebar: true,
29345         autoScroll:true,
29346         resizeTabs: true,
29347         minTabWidth: 50,
29348         preferredTabWidth: 150,
29349         panels: [
29350             new CP("center1", {title: "Close Me", closable: true}),
29351             new CP("center2", {title: "Center Panel", closable: false})
29352         ]
29353     }
29354 }, document.body);
29355
29356 layout.getRegion("center").showPanel("center1");
29357 </code></pre>
29358  * @param config
29359  * @param targetEl
29360  */
29361 Roo.BorderLayout.create = function(config, targetEl){
29362     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29363     layout.beginUpdate();
29364     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29365     for(var j = 0, jlen = regions.length; j < jlen; j++){
29366         var lr = regions[j];
29367         if(layout.regions[lr] && config[lr].panels){
29368             var r = layout.regions[lr];
29369             var ps = config[lr].panels;
29370             layout.addTypedPanels(r, ps);
29371         }
29372     }
29373     layout.endUpdate();
29374     return layout;
29375 };
29376
29377 // private
29378 Roo.BorderLayout.RegionFactory = {
29379     // private
29380     validRegions : ["north","south","east","west","center"],
29381
29382     // private
29383     create : function(target, mgr, config){
29384         target = target.toLowerCase();
29385         if(config.lightweight || config.basic){
29386             return new Roo.BasicLayoutRegion(mgr, config, target);
29387         }
29388         switch(target){
29389             case "north":
29390                 return new Roo.NorthLayoutRegion(mgr, config);
29391             case "south":
29392                 return new Roo.SouthLayoutRegion(mgr, config);
29393             case "east":
29394                 return new Roo.EastLayoutRegion(mgr, config);
29395             case "west":
29396                 return new Roo.WestLayoutRegion(mgr, config);
29397             case "center":
29398                 return new Roo.CenterLayoutRegion(mgr, config);
29399         }
29400         throw 'Layout region "'+target+'" not supported.';
29401     }
29402 };/*
29403  * Based on:
29404  * Ext JS Library 1.1.1
29405  * Copyright(c) 2006-2007, Ext JS, LLC.
29406  *
29407  * Originally Released Under LGPL - original licence link has changed is not relivant.
29408  *
29409  * Fork - LGPL
29410  * <script type="text/javascript">
29411  */
29412  
29413 /**
29414  * @class Roo.BasicLayoutRegion
29415  * @extends Roo.util.Observable
29416  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29417  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29418  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29419  */
29420 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29421     this.mgr = mgr;
29422     this.position  = pos;
29423     this.events = {
29424         /**
29425          * @scope Roo.BasicLayoutRegion
29426          */
29427         
29428         /**
29429          * @event beforeremove
29430          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29431          * @param {Roo.LayoutRegion} this
29432          * @param {Roo.ContentPanel} panel The panel
29433          * @param {Object} e The cancel event object
29434          */
29435         "beforeremove" : true,
29436         /**
29437          * @event invalidated
29438          * Fires when the layout for this region is changed.
29439          * @param {Roo.LayoutRegion} this
29440          */
29441         "invalidated" : true,
29442         /**
29443          * @event visibilitychange
29444          * Fires when this region is shown or hidden 
29445          * @param {Roo.LayoutRegion} this
29446          * @param {Boolean} visibility true or false
29447          */
29448         "visibilitychange" : true,
29449         /**
29450          * @event paneladded
29451          * Fires when a panel is added. 
29452          * @param {Roo.LayoutRegion} this
29453          * @param {Roo.ContentPanel} panel The panel
29454          */
29455         "paneladded" : true,
29456         /**
29457          * @event panelremoved
29458          * Fires when a panel is removed. 
29459          * @param {Roo.LayoutRegion} this
29460          * @param {Roo.ContentPanel} panel The panel
29461          */
29462         "panelremoved" : true,
29463         /**
29464          * @event collapsed
29465          * Fires when this region is collapsed.
29466          * @param {Roo.LayoutRegion} this
29467          */
29468         "collapsed" : true,
29469         /**
29470          * @event expanded
29471          * Fires when this region is expanded.
29472          * @param {Roo.LayoutRegion} this
29473          */
29474         "expanded" : true,
29475         /**
29476          * @event slideshow
29477          * Fires when this region is slid into view.
29478          * @param {Roo.LayoutRegion} this
29479          */
29480         "slideshow" : true,
29481         /**
29482          * @event slidehide
29483          * Fires when this region slides out of view. 
29484          * @param {Roo.LayoutRegion} this
29485          */
29486         "slidehide" : true,
29487         /**
29488          * @event panelactivated
29489          * Fires when a panel is activated. 
29490          * @param {Roo.LayoutRegion} this
29491          * @param {Roo.ContentPanel} panel The activated panel
29492          */
29493         "panelactivated" : true,
29494         /**
29495          * @event resized
29496          * Fires when the user resizes this region. 
29497          * @param {Roo.LayoutRegion} this
29498          * @param {Number} newSize The new size (width for east/west, height for north/south)
29499          */
29500         "resized" : true
29501     };
29502     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29503     this.panels = new Roo.util.MixedCollection();
29504     this.panels.getKey = this.getPanelId.createDelegate(this);
29505     this.box = null;
29506     this.activePanel = null;
29507     // ensure listeners are added...
29508     
29509     if (config.listeners || config.events) {
29510         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29511             listeners : config.listeners || {},
29512             events : config.events || {}
29513         });
29514     }
29515     
29516     if(skipConfig !== true){
29517         this.applyConfig(config);
29518     }
29519 };
29520
29521 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29522     getPanelId : function(p){
29523         return p.getId();
29524     },
29525     
29526     applyConfig : function(config){
29527         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29528         this.config = config;
29529         
29530     },
29531     
29532     /**
29533      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29534      * the width, for horizontal (north, south) the height.
29535      * @param {Number} newSize The new width or height
29536      */
29537     resizeTo : function(newSize){
29538         var el = this.el ? this.el :
29539                  (this.activePanel ? this.activePanel.getEl() : null);
29540         if(el){
29541             switch(this.position){
29542                 case "east":
29543                 case "west":
29544                     el.setWidth(newSize);
29545                     this.fireEvent("resized", this, newSize);
29546                 break;
29547                 case "north":
29548                 case "south":
29549                     el.setHeight(newSize);
29550                     this.fireEvent("resized", this, newSize);
29551                 break;                
29552             }
29553         }
29554     },
29555     
29556     getBox : function(){
29557         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29558     },
29559     
29560     getMargins : function(){
29561         return this.margins;
29562     },
29563     
29564     updateBox : function(box){
29565         this.box = box;
29566         var el = this.activePanel.getEl();
29567         el.dom.style.left = box.x + "px";
29568         el.dom.style.top = box.y + "px";
29569         this.activePanel.setSize(box.width, box.height);
29570     },
29571     
29572     /**
29573      * Returns the container element for this region.
29574      * @return {Roo.Element}
29575      */
29576     getEl : function(){
29577         return this.activePanel;
29578     },
29579     
29580     /**
29581      * Returns true if this region is currently visible.
29582      * @return {Boolean}
29583      */
29584     isVisible : function(){
29585         return this.activePanel ? true : false;
29586     },
29587     
29588     setActivePanel : function(panel){
29589         panel = this.getPanel(panel);
29590         if(this.activePanel && this.activePanel != panel){
29591             this.activePanel.setActiveState(false);
29592             this.activePanel.getEl().setLeftTop(-10000,-10000);
29593         }
29594         this.activePanel = panel;
29595         panel.setActiveState(true);
29596         if(this.box){
29597             panel.setSize(this.box.width, this.box.height);
29598         }
29599         this.fireEvent("panelactivated", this, panel);
29600         this.fireEvent("invalidated");
29601     },
29602     
29603     /**
29604      * Show the specified panel.
29605      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29606      * @return {Roo.ContentPanel} The shown panel or null
29607      */
29608     showPanel : function(panel){
29609         if(panel = this.getPanel(panel)){
29610             this.setActivePanel(panel);
29611         }
29612         return panel;
29613     },
29614     
29615     /**
29616      * Get the active panel for this region.
29617      * @return {Roo.ContentPanel} The active panel or null
29618      */
29619     getActivePanel : function(){
29620         return this.activePanel;
29621     },
29622     
29623     /**
29624      * Add the passed ContentPanel(s)
29625      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29626      * @return {Roo.ContentPanel} The panel added (if only one was added)
29627      */
29628     add : function(panel){
29629         if(arguments.length > 1){
29630             for(var i = 0, len = arguments.length; i < len; i++) {
29631                 this.add(arguments[i]);
29632             }
29633             return null;
29634         }
29635         if(this.hasPanel(panel)){
29636             this.showPanel(panel);
29637             return panel;
29638         }
29639         var el = panel.getEl();
29640         if(el.dom.parentNode != this.mgr.el.dom){
29641             this.mgr.el.dom.appendChild(el.dom);
29642         }
29643         if(panel.setRegion){
29644             panel.setRegion(this);
29645         }
29646         this.panels.add(panel);
29647         el.setStyle("position", "absolute");
29648         if(!panel.background){
29649             this.setActivePanel(panel);
29650             if(this.config.initialSize && this.panels.getCount()==1){
29651                 this.resizeTo(this.config.initialSize);
29652             }
29653         }
29654         this.fireEvent("paneladded", this, panel);
29655         return panel;
29656     },
29657     
29658     /**
29659      * Returns true if the panel is in this region.
29660      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29661      * @return {Boolean}
29662      */
29663     hasPanel : function(panel){
29664         if(typeof panel == "object"){ // must be panel obj
29665             panel = panel.getId();
29666         }
29667         return this.getPanel(panel) ? true : false;
29668     },
29669     
29670     /**
29671      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29672      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29673      * @param {Boolean} preservePanel Overrides the config preservePanel option
29674      * @return {Roo.ContentPanel} The panel that was removed
29675      */
29676     remove : function(panel, preservePanel){
29677         panel = this.getPanel(panel);
29678         if(!panel){
29679             return null;
29680         }
29681         var e = {};
29682         this.fireEvent("beforeremove", this, panel, e);
29683         if(e.cancel === true){
29684             return null;
29685         }
29686         var panelId = panel.getId();
29687         this.panels.removeKey(panelId);
29688         return panel;
29689     },
29690     
29691     /**
29692      * Returns the panel specified or null if it's not in this region.
29693      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29694      * @return {Roo.ContentPanel}
29695      */
29696     getPanel : function(id){
29697         if(typeof id == "object"){ // must be panel obj
29698             return id;
29699         }
29700         return this.panels.get(id);
29701     },
29702     
29703     /**
29704      * Returns this regions position (north/south/east/west/center).
29705      * @return {String} 
29706      */
29707     getPosition: function(){
29708         return this.position;    
29709     }
29710 });/*
29711  * Based on:
29712  * Ext JS Library 1.1.1
29713  * Copyright(c) 2006-2007, Ext JS, LLC.
29714  *
29715  * Originally Released Under LGPL - original licence link has changed is not relivant.
29716  *
29717  * Fork - LGPL
29718  * <script type="text/javascript">
29719  */
29720  
29721 /**
29722  * @class Roo.LayoutRegion
29723  * @extends Roo.BasicLayoutRegion
29724  * This class represents a region in a layout manager.
29725  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
29726  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
29727  * @cfg {Boolean} floatable False to disable floating (defaults to true)
29728  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29729  * @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})
29730  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
29731  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
29732  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
29733  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
29734  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
29735  * @cfg {String} title The title for the region (overrides panel titles)
29736  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
29737  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29738  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
29739  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29740  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
29741  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29742  * the space available, similar to FireFox 1.5 tabs (defaults to false)
29743  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
29744  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
29745  * @cfg {Boolean} showPin True to show a pin button
29746 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
29747 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
29748 * @cfg {Boolean} disableTabTips True to disable tab tooltips
29749 * @cfg {Number} width  For East/West panels
29750 * @cfg {Number} height For North/South panels
29751 * @cfg {Boolean} split To show the splitter
29752  */
29753 Roo.LayoutRegion = function(mgr, config, pos){
29754     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29755     var dh = Roo.DomHelper;
29756     /** This region's container element 
29757     * @type Roo.Element */
29758     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29759     /** This region's title element 
29760     * @type Roo.Element */
29761
29762     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29763         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29764         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29765     ]}, true);
29766     this.titleEl.enableDisplayMode();
29767     /** This region's title text element 
29768     * @type HTMLElement */
29769     this.titleTextEl = this.titleEl.dom.firstChild;
29770     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29771     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29772     this.closeBtn.enableDisplayMode();
29773     this.closeBtn.on("click", this.closeClicked, this);
29774     this.closeBtn.hide();
29775
29776     this.createBody(config);
29777     this.visible = true;
29778     this.collapsed = false;
29779
29780     if(config.hideWhenEmpty){
29781         this.hide();
29782         this.on("paneladded", this.validateVisibility, this);
29783         this.on("panelremoved", this.validateVisibility, this);
29784     }
29785     this.applyConfig(config);
29786 };
29787
29788 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29789
29790     createBody : function(){
29791         /** This region's body element 
29792         * @type Roo.Element */
29793         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29794     },
29795
29796     applyConfig : function(c){
29797         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29798             var dh = Roo.DomHelper;
29799             if(c.titlebar !== false){
29800                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29801                 this.collapseBtn.on("click", this.collapse, this);
29802                 this.collapseBtn.enableDisplayMode();
29803
29804                 if(c.showPin === true || this.showPin){
29805                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29806                     this.stickBtn.enableDisplayMode();
29807                     this.stickBtn.on("click", this.expand, this);
29808                     this.stickBtn.hide();
29809                 }
29810             }
29811             /** This region's collapsed element
29812             * @type Roo.Element */
29813             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29814                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29815             ]}, true);
29816             if(c.floatable !== false){
29817                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29818                this.collapsedEl.on("click", this.collapseClick, this);
29819             }
29820
29821             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29822                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29823                    id: "message", unselectable: "on", style:{"float":"left"}});
29824                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29825              }
29826             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29827             this.expandBtn.on("click", this.expand, this);
29828         }
29829         if(this.collapseBtn){
29830             this.collapseBtn.setVisible(c.collapsible == true);
29831         }
29832         this.cmargins = c.cmargins || this.cmargins ||
29833                          (this.position == "west" || this.position == "east" ?
29834                              {top: 0, left: 2, right:2, bottom: 0} :
29835                              {top: 2, left: 0, right:0, bottom: 2});
29836         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29837         this.bottomTabs = c.tabPosition != "top";
29838         this.autoScroll = c.autoScroll || false;
29839         if(this.autoScroll){
29840             this.bodyEl.setStyle("overflow", "auto");
29841         }else{
29842             this.bodyEl.setStyle("overflow", "hidden");
29843         }
29844         //if(c.titlebar !== false){
29845             if((!c.titlebar && !c.title) || c.titlebar === false){
29846                 this.titleEl.hide();
29847             }else{
29848                 this.titleEl.show();
29849                 if(c.title){
29850                     this.titleTextEl.innerHTML = c.title;
29851                 }
29852             }
29853         //}
29854         this.duration = c.duration || .30;
29855         this.slideDuration = c.slideDuration || .45;
29856         this.config = c;
29857         if(c.collapsed){
29858             this.collapse(true);
29859         }
29860         if(c.hidden){
29861             this.hide();
29862         }
29863     },
29864     /**
29865      * Returns true if this region is currently visible.
29866      * @return {Boolean}
29867      */
29868     isVisible : function(){
29869         return this.visible;
29870     },
29871
29872     /**
29873      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29874      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29875      */
29876     setCollapsedTitle : function(title){
29877         title = title || "&#160;";
29878         if(this.collapsedTitleTextEl){
29879             this.collapsedTitleTextEl.innerHTML = title;
29880         }
29881     },
29882
29883     getBox : function(){
29884         var b;
29885         if(!this.collapsed){
29886             b = this.el.getBox(false, true);
29887         }else{
29888             b = this.collapsedEl.getBox(false, true);
29889         }
29890         return b;
29891     },
29892
29893     getMargins : function(){
29894         return this.collapsed ? this.cmargins : this.margins;
29895     },
29896
29897     highlight : function(){
29898         this.el.addClass("x-layout-panel-dragover");
29899     },
29900
29901     unhighlight : function(){
29902         this.el.removeClass("x-layout-panel-dragover");
29903     },
29904
29905     updateBox : function(box){
29906         this.box = box;
29907         if(!this.collapsed){
29908             this.el.dom.style.left = box.x + "px";
29909             this.el.dom.style.top = box.y + "px";
29910             this.updateBody(box.width, box.height);
29911         }else{
29912             this.collapsedEl.dom.style.left = box.x + "px";
29913             this.collapsedEl.dom.style.top = box.y + "px";
29914             this.collapsedEl.setSize(box.width, box.height);
29915         }
29916         if(this.tabs){
29917             this.tabs.autoSizeTabs();
29918         }
29919     },
29920
29921     updateBody : function(w, h){
29922         if(w !== null){
29923             this.el.setWidth(w);
29924             w -= this.el.getBorderWidth("rl");
29925             if(this.config.adjustments){
29926                 w += this.config.adjustments[0];
29927             }
29928         }
29929         if(h !== null){
29930             this.el.setHeight(h);
29931             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29932             h -= this.el.getBorderWidth("tb");
29933             if(this.config.adjustments){
29934                 h += this.config.adjustments[1];
29935             }
29936             this.bodyEl.setHeight(h);
29937             if(this.tabs){
29938                 h = this.tabs.syncHeight(h);
29939             }
29940         }
29941         if(this.panelSize){
29942             w = w !== null ? w : this.panelSize.width;
29943             h = h !== null ? h : this.panelSize.height;
29944         }
29945         if(this.activePanel){
29946             var el = this.activePanel.getEl();
29947             w = w !== null ? w : el.getWidth();
29948             h = h !== null ? h : el.getHeight();
29949             this.panelSize = {width: w, height: h};
29950             this.activePanel.setSize(w, h);
29951         }
29952         if(Roo.isIE && this.tabs){
29953             this.tabs.el.repaint();
29954         }
29955     },
29956
29957     /**
29958      * Returns the container element for this region.
29959      * @return {Roo.Element}
29960      */
29961     getEl : function(){
29962         return this.el;
29963     },
29964
29965     /**
29966      * Hides this region.
29967      */
29968     hide : function(){
29969         if(!this.collapsed){
29970             this.el.dom.style.left = "-2000px";
29971             this.el.hide();
29972         }else{
29973             this.collapsedEl.dom.style.left = "-2000px";
29974             this.collapsedEl.hide();
29975         }
29976         this.visible = false;
29977         this.fireEvent("visibilitychange", this, false);
29978     },
29979
29980     /**
29981      * Shows this region if it was previously hidden.
29982      */
29983     show : function(){
29984         if(!this.collapsed){
29985             this.el.show();
29986         }else{
29987             this.collapsedEl.show();
29988         }
29989         this.visible = true;
29990         this.fireEvent("visibilitychange", this, true);
29991     },
29992
29993     closeClicked : function(){
29994         if(this.activePanel){
29995             this.remove(this.activePanel);
29996         }
29997     },
29998
29999     collapseClick : function(e){
30000         if(this.isSlid){
30001            e.stopPropagation();
30002            this.slideIn();
30003         }else{
30004            e.stopPropagation();
30005            this.slideOut();
30006         }
30007     },
30008
30009     /**
30010      * Collapses this region.
30011      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30012      */
30013     collapse : function(skipAnim){
30014         if(this.collapsed) return;
30015         this.collapsed = true;
30016         if(this.split){
30017             this.split.el.hide();
30018         }
30019         if(this.config.animate && skipAnim !== true){
30020             this.fireEvent("invalidated", this);
30021             this.animateCollapse();
30022         }else{
30023             this.el.setLocation(-20000,-20000);
30024             this.el.hide();
30025             this.collapsedEl.show();
30026             this.fireEvent("collapsed", this);
30027             this.fireEvent("invalidated", this);
30028         }
30029     },
30030
30031     animateCollapse : function(){
30032         // overridden
30033     },
30034
30035     /**
30036      * Expands this region if it was previously collapsed.
30037      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30038      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30039      */
30040     expand : function(e, skipAnim){
30041         if(e) e.stopPropagation();
30042         if(!this.collapsed || this.el.hasActiveFx()) return;
30043         if(this.isSlid){
30044             this.afterSlideIn();
30045             skipAnim = true;
30046         }
30047         this.collapsed = false;
30048         if(this.config.animate && skipAnim !== true){
30049             this.animateExpand();
30050         }else{
30051             this.el.show();
30052             if(this.split){
30053                 this.split.el.show();
30054             }
30055             this.collapsedEl.setLocation(-2000,-2000);
30056             this.collapsedEl.hide();
30057             this.fireEvent("invalidated", this);
30058             this.fireEvent("expanded", this);
30059         }
30060     },
30061
30062     animateExpand : function(){
30063         // overridden
30064     },
30065
30066     initTabs : function(){
30067         this.bodyEl.setStyle("overflow", "hidden");
30068         var ts = new Roo.TabPanel(this.bodyEl.dom, {
30069             tabPosition: this.bottomTabs ? 'bottom' : 'top',
30070             disableTooltips: this.config.disableTabTips
30071         });
30072         if(this.config.hideTabs){
30073             ts.stripWrap.setDisplayed(false);
30074         }
30075         this.tabs = ts;
30076         ts.resizeTabs = this.config.resizeTabs === true;
30077         ts.minTabWidth = this.config.minTabWidth || 40;
30078         ts.maxTabWidth = this.config.maxTabWidth || 250;
30079         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30080         ts.monitorResize = false;
30081         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30082         ts.bodyEl.addClass('x-layout-tabs-body');
30083         this.panels.each(this.initPanelAsTab, this);
30084     },
30085
30086     initPanelAsTab : function(panel){
30087         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30088                     this.config.closeOnTab && panel.isClosable());
30089         if(panel.tabTip !== undefined){
30090             ti.setTooltip(panel.tabTip);
30091         }
30092         ti.on("activate", function(){
30093               this.setActivePanel(panel);
30094         }, this);
30095         if(this.config.closeOnTab){
30096             ti.on("beforeclose", function(t, e){
30097                 e.cancel = true;
30098                 this.remove(panel);
30099             }, this);
30100         }
30101         return ti;
30102     },
30103
30104     updatePanelTitle : function(panel, title){
30105         if(this.activePanel == panel){
30106             this.updateTitle(title);
30107         }
30108         if(this.tabs){
30109             var ti = this.tabs.getTab(panel.getEl().id);
30110             ti.setText(title);
30111             if(panel.tabTip !== undefined){
30112                 ti.setTooltip(panel.tabTip);
30113             }
30114         }
30115     },
30116
30117     updateTitle : function(title){
30118         if(this.titleTextEl && !this.config.title){
30119             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30120         }
30121     },
30122
30123     setActivePanel : function(panel){
30124         panel = this.getPanel(panel);
30125         if(this.activePanel && this.activePanel != panel){
30126             this.activePanel.setActiveState(false);
30127         }
30128         this.activePanel = panel;
30129         panel.setActiveState(true);
30130         if(this.panelSize){
30131             panel.setSize(this.panelSize.width, this.panelSize.height);
30132         }
30133         if(this.closeBtn){
30134             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30135         }
30136         this.updateTitle(panel.getTitle());
30137         if(this.tabs){
30138             this.fireEvent("invalidated", this);
30139         }
30140         this.fireEvent("panelactivated", this, panel);
30141     },
30142
30143     /**
30144      * Shows the specified panel.
30145      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30146      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30147      */
30148     showPanel : function(panel){
30149         if(panel = this.getPanel(panel)){
30150             if(this.tabs){
30151                 var tab = this.tabs.getTab(panel.getEl().id);
30152                 if(tab.isHidden()){
30153                     this.tabs.unhideTab(tab.id);
30154                 }
30155                 tab.activate();
30156             }else{
30157                 this.setActivePanel(panel);
30158             }
30159         }
30160         return panel;
30161     },
30162
30163     /**
30164      * Get the active panel for this region.
30165      * @return {Roo.ContentPanel} The active panel or null
30166      */
30167     getActivePanel : function(){
30168         return this.activePanel;
30169     },
30170
30171     validateVisibility : function(){
30172         if(this.panels.getCount() < 1){
30173             this.updateTitle("&#160;");
30174             this.closeBtn.hide();
30175             this.hide();
30176         }else{
30177             if(!this.isVisible()){
30178                 this.show();
30179             }
30180         }
30181     },
30182
30183     /**
30184      * Adds the passed ContentPanel(s) to this region.
30185      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30186      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30187      */
30188     add : function(panel){
30189         if(arguments.length > 1){
30190             for(var i = 0, len = arguments.length; i < len; i++) {
30191                 this.add(arguments[i]);
30192             }
30193             return null;
30194         }
30195         if(this.hasPanel(panel)){
30196             this.showPanel(panel);
30197             return panel;
30198         }
30199         panel.setRegion(this);
30200         this.panels.add(panel);
30201         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30202             this.bodyEl.dom.appendChild(panel.getEl().dom);
30203             if(panel.background !== true){
30204                 this.setActivePanel(panel);
30205             }
30206             this.fireEvent("paneladded", this, panel);
30207             return panel;
30208         }
30209         if(!this.tabs){
30210             this.initTabs();
30211         }else{
30212             this.initPanelAsTab(panel);
30213         }
30214         if(panel.background !== true){
30215             this.tabs.activate(panel.getEl().id);
30216         }
30217         this.fireEvent("paneladded", this, panel);
30218         return panel;
30219     },
30220
30221     /**
30222      * Hides the tab for the specified panel.
30223      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30224      */
30225     hidePanel : function(panel){
30226         if(this.tabs && (panel = this.getPanel(panel))){
30227             this.tabs.hideTab(panel.getEl().id);
30228         }
30229     },
30230
30231     /**
30232      * Unhides the tab for a previously hidden panel.
30233      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30234      */
30235     unhidePanel : function(panel){
30236         if(this.tabs && (panel = this.getPanel(panel))){
30237             this.tabs.unhideTab(panel.getEl().id);
30238         }
30239     },
30240
30241     clearPanels : function(){
30242         while(this.panels.getCount() > 0){
30243              this.remove(this.panels.first());
30244         }
30245     },
30246
30247     /**
30248      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30249      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30250      * @param {Boolean} preservePanel Overrides the config preservePanel option
30251      * @return {Roo.ContentPanel} The panel that was removed
30252      */
30253     remove : function(panel, preservePanel){
30254         panel = this.getPanel(panel);
30255         if(!panel){
30256             return null;
30257         }
30258         var e = {};
30259         this.fireEvent("beforeremove", this, panel, e);
30260         if(e.cancel === true){
30261             return null;
30262         }
30263         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30264         var panelId = panel.getId();
30265         this.panels.removeKey(panelId);
30266         if(preservePanel){
30267             document.body.appendChild(panel.getEl().dom);
30268         }
30269         if(this.tabs){
30270             this.tabs.removeTab(panel.getEl().id);
30271         }else if (!preservePanel){
30272             this.bodyEl.dom.removeChild(panel.getEl().dom);
30273         }
30274         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30275             var p = this.panels.first();
30276             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30277             tempEl.appendChild(p.getEl().dom);
30278             this.bodyEl.update("");
30279             this.bodyEl.dom.appendChild(p.getEl().dom);
30280             tempEl = null;
30281             this.updateTitle(p.getTitle());
30282             this.tabs = null;
30283             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30284             this.setActivePanel(p);
30285         }
30286         panel.setRegion(null);
30287         if(this.activePanel == panel){
30288             this.activePanel = null;
30289         }
30290         if(this.config.autoDestroy !== false && preservePanel !== true){
30291             try{panel.destroy();}catch(e){}
30292         }
30293         this.fireEvent("panelremoved", this, panel);
30294         return panel;
30295     },
30296
30297     /**
30298      * Returns the TabPanel component used by this region
30299      * @return {Roo.TabPanel}
30300      */
30301     getTabs : function(){
30302         return this.tabs;
30303     },
30304
30305     createTool : function(parentEl, className){
30306         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30307             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30308         btn.addClassOnOver("x-layout-tools-button-over");
30309         return btn;
30310     }
30311 });/*
30312  * Based on:
30313  * Ext JS Library 1.1.1
30314  * Copyright(c) 2006-2007, Ext JS, LLC.
30315  *
30316  * Originally Released Under LGPL - original licence link has changed is not relivant.
30317  *
30318  * Fork - LGPL
30319  * <script type="text/javascript">
30320  */
30321  
30322
30323
30324 /**
30325  * @class Roo.SplitLayoutRegion
30326  * @extends Roo.LayoutRegion
30327  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30328  */
30329 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30330     this.cursor = cursor;
30331     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30332 };
30333
30334 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30335     splitTip : "Drag to resize.",
30336     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30337     useSplitTips : false,
30338
30339     applyConfig : function(config){
30340         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30341         if(config.split){
30342             if(!this.split){
30343                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30344                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30345                 /** The SplitBar for this region 
30346                 * @type Roo.SplitBar */
30347                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30348                 this.split.on("moved", this.onSplitMove, this);
30349                 this.split.useShim = config.useShim === true;
30350                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30351                 if(this.useSplitTips){
30352                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30353                 }
30354                 if(config.collapsible){
30355                     this.split.el.on("dblclick", this.collapse,  this);
30356                 }
30357             }
30358             if(typeof config.minSize != "undefined"){
30359                 this.split.minSize = config.minSize;
30360             }
30361             if(typeof config.maxSize != "undefined"){
30362                 this.split.maxSize = config.maxSize;
30363             }
30364             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30365                 this.hideSplitter();
30366             }
30367         }
30368     },
30369
30370     getHMaxSize : function(){
30371          var cmax = this.config.maxSize || 10000;
30372          var center = this.mgr.getRegion("center");
30373          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30374     },
30375
30376     getVMaxSize : function(){
30377          var cmax = this.config.maxSize || 10000;
30378          var center = this.mgr.getRegion("center");
30379          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30380     },
30381
30382     onSplitMove : function(split, newSize){
30383         this.fireEvent("resized", this, newSize);
30384     },
30385     
30386     /** 
30387      * Returns the {@link Roo.SplitBar} for this region.
30388      * @return {Roo.SplitBar}
30389      */
30390     getSplitBar : function(){
30391         return this.split;
30392     },
30393     
30394     hide : function(){
30395         this.hideSplitter();
30396         Roo.SplitLayoutRegion.superclass.hide.call(this);
30397     },
30398
30399     hideSplitter : function(){
30400         if(this.split){
30401             this.split.el.setLocation(-2000,-2000);
30402             this.split.el.hide();
30403         }
30404     },
30405
30406     show : function(){
30407         if(this.split){
30408             this.split.el.show();
30409         }
30410         Roo.SplitLayoutRegion.superclass.show.call(this);
30411     },
30412     
30413     beforeSlide: function(){
30414         if(Roo.isGecko){// firefox overflow auto bug workaround
30415             this.bodyEl.clip();
30416             if(this.tabs) this.tabs.bodyEl.clip();
30417             if(this.activePanel){
30418                 this.activePanel.getEl().clip();
30419                 
30420                 if(this.activePanel.beforeSlide){
30421                     this.activePanel.beforeSlide();
30422                 }
30423             }
30424         }
30425     },
30426     
30427     afterSlide : function(){
30428         if(Roo.isGecko){// firefox overflow auto bug workaround
30429             this.bodyEl.unclip();
30430             if(this.tabs) this.tabs.bodyEl.unclip();
30431             if(this.activePanel){
30432                 this.activePanel.getEl().unclip();
30433                 if(this.activePanel.afterSlide){
30434                     this.activePanel.afterSlide();
30435                 }
30436             }
30437         }
30438     },
30439
30440     initAutoHide : function(){
30441         if(this.autoHide !== false){
30442             if(!this.autoHideHd){
30443                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30444                 this.autoHideHd = {
30445                     "mouseout": function(e){
30446                         if(!e.within(this.el, true)){
30447                             st.delay(500);
30448                         }
30449                     },
30450                     "mouseover" : function(e){
30451                         st.cancel();
30452                     },
30453                     scope : this
30454                 };
30455             }
30456             this.el.on(this.autoHideHd);
30457         }
30458     },
30459
30460     clearAutoHide : function(){
30461         if(this.autoHide !== false){
30462             this.el.un("mouseout", this.autoHideHd.mouseout);
30463             this.el.un("mouseover", this.autoHideHd.mouseover);
30464         }
30465     },
30466
30467     clearMonitor : function(){
30468         Roo.get(document).un("click", this.slideInIf, this);
30469     },
30470
30471     // these names are backwards but not changed for compat
30472     slideOut : function(){
30473         if(this.isSlid || this.el.hasActiveFx()){
30474             return;
30475         }
30476         this.isSlid = true;
30477         if(this.collapseBtn){
30478             this.collapseBtn.hide();
30479         }
30480         this.closeBtnState = this.closeBtn.getStyle('display');
30481         this.closeBtn.hide();
30482         if(this.stickBtn){
30483             this.stickBtn.show();
30484         }
30485         this.el.show();
30486         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30487         this.beforeSlide();
30488         this.el.setStyle("z-index", 10001);
30489         this.el.slideIn(this.getSlideAnchor(), {
30490             callback: function(){
30491                 this.afterSlide();
30492                 this.initAutoHide();
30493                 Roo.get(document).on("click", this.slideInIf, this);
30494                 this.fireEvent("slideshow", this);
30495             },
30496             scope: this,
30497             block: true
30498         });
30499     },
30500
30501     afterSlideIn : function(){
30502         this.clearAutoHide();
30503         this.isSlid = false;
30504         this.clearMonitor();
30505         this.el.setStyle("z-index", "");
30506         if(this.collapseBtn){
30507             this.collapseBtn.show();
30508         }
30509         this.closeBtn.setStyle('display', this.closeBtnState);
30510         if(this.stickBtn){
30511             this.stickBtn.hide();
30512         }
30513         this.fireEvent("slidehide", this);
30514     },
30515
30516     slideIn : function(cb){
30517         if(!this.isSlid || this.el.hasActiveFx()){
30518             Roo.callback(cb);
30519             return;
30520         }
30521         this.isSlid = false;
30522         this.beforeSlide();
30523         this.el.slideOut(this.getSlideAnchor(), {
30524             callback: function(){
30525                 this.el.setLeftTop(-10000, -10000);
30526                 this.afterSlide();
30527                 this.afterSlideIn();
30528                 Roo.callback(cb);
30529             },
30530             scope: this,
30531             block: true
30532         });
30533     },
30534     
30535     slideInIf : function(e){
30536         if(!e.within(this.el)){
30537             this.slideIn();
30538         }
30539     },
30540
30541     animateCollapse : function(){
30542         this.beforeSlide();
30543         this.el.setStyle("z-index", 20000);
30544         var anchor = this.getSlideAnchor();
30545         this.el.slideOut(anchor, {
30546             callback : function(){
30547                 this.el.setStyle("z-index", "");
30548                 this.collapsedEl.slideIn(anchor, {duration:.3});
30549                 this.afterSlide();
30550                 this.el.setLocation(-10000,-10000);
30551                 this.el.hide();
30552                 this.fireEvent("collapsed", this);
30553             },
30554             scope: this,
30555             block: true
30556         });
30557     },
30558
30559     animateExpand : function(){
30560         this.beforeSlide();
30561         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30562         this.el.setStyle("z-index", 20000);
30563         this.collapsedEl.hide({
30564             duration:.1
30565         });
30566         this.el.slideIn(this.getSlideAnchor(), {
30567             callback : function(){
30568                 this.el.setStyle("z-index", "");
30569                 this.afterSlide();
30570                 if(this.split){
30571                     this.split.el.show();
30572                 }
30573                 this.fireEvent("invalidated", this);
30574                 this.fireEvent("expanded", this);
30575             },
30576             scope: this,
30577             block: true
30578         });
30579     },
30580
30581     anchors : {
30582         "west" : "left",
30583         "east" : "right",
30584         "north" : "top",
30585         "south" : "bottom"
30586     },
30587
30588     sanchors : {
30589         "west" : "l",
30590         "east" : "r",
30591         "north" : "t",
30592         "south" : "b"
30593     },
30594
30595     canchors : {
30596         "west" : "tl-tr",
30597         "east" : "tr-tl",
30598         "north" : "tl-bl",
30599         "south" : "bl-tl"
30600     },
30601
30602     getAnchor : function(){
30603         return this.anchors[this.position];
30604     },
30605
30606     getCollapseAnchor : function(){
30607         return this.canchors[this.position];
30608     },
30609
30610     getSlideAnchor : function(){
30611         return this.sanchors[this.position];
30612     },
30613
30614     getAlignAdj : function(){
30615         var cm = this.cmargins;
30616         switch(this.position){
30617             case "west":
30618                 return [0, 0];
30619             break;
30620             case "east":
30621                 return [0, 0];
30622             break;
30623             case "north":
30624                 return [0, 0];
30625             break;
30626             case "south":
30627                 return [0, 0];
30628             break;
30629         }
30630     },
30631
30632     getExpandAdj : function(){
30633         var c = this.collapsedEl, cm = this.cmargins;
30634         switch(this.position){
30635             case "west":
30636                 return [-(cm.right+c.getWidth()+cm.left), 0];
30637             break;
30638             case "east":
30639                 return [cm.right+c.getWidth()+cm.left, 0];
30640             break;
30641             case "north":
30642                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30643             break;
30644             case "south":
30645                 return [0, cm.top+cm.bottom+c.getHeight()];
30646             break;
30647         }
30648     }
30649 });/*
30650  * Based on:
30651  * Ext JS Library 1.1.1
30652  * Copyright(c) 2006-2007, Ext JS, LLC.
30653  *
30654  * Originally Released Under LGPL - original licence link has changed is not relivant.
30655  *
30656  * Fork - LGPL
30657  * <script type="text/javascript">
30658  */
30659 /*
30660  * These classes are private internal classes
30661  */
30662 Roo.CenterLayoutRegion = function(mgr, config){
30663     Roo.LayoutRegion.call(this, mgr, config, "center");
30664     this.visible = true;
30665     this.minWidth = config.minWidth || 20;
30666     this.minHeight = config.minHeight || 20;
30667 };
30668
30669 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30670     hide : function(){
30671         // center panel can't be hidden
30672     },
30673     
30674     show : function(){
30675         // center panel can't be hidden
30676     },
30677     
30678     getMinWidth: function(){
30679         return this.minWidth;
30680     },
30681     
30682     getMinHeight: function(){
30683         return this.minHeight;
30684     }
30685 });
30686
30687
30688 Roo.NorthLayoutRegion = function(mgr, config){
30689     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30690     if(this.split){
30691         this.split.placement = Roo.SplitBar.TOP;
30692         this.split.orientation = Roo.SplitBar.VERTICAL;
30693         this.split.el.addClass("x-layout-split-v");
30694     }
30695     var size = config.initialSize || config.height;
30696     if(typeof size != "undefined"){
30697         this.el.setHeight(size);
30698     }
30699 };
30700 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30701     orientation: Roo.SplitBar.VERTICAL,
30702     getBox : function(){
30703         if(this.collapsed){
30704             return this.collapsedEl.getBox();
30705         }
30706         var box = this.el.getBox();
30707         if(this.split){
30708             box.height += this.split.el.getHeight();
30709         }
30710         return box;
30711     },
30712     
30713     updateBox : function(box){
30714         if(this.split && !this.collapsed){
30715             box.height -= this.split.el.getHeight();
30716             this.split.el.setLeft(box.x);
30717             this.split.el.setTop(box.y+box.height);
30718             this.split.el.setWidth(box.width);
30719         }
30720         if(this.collapsed){
30721             this.updateBody(box.width, null);
30722         }
30723         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30724     }
30725 });
30726
30727 Roo.SouthLayoutRegion = function(mgr, config){
30728     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30729     if(this.split){
30730         this.split.placement = Roo.SplitBar.BOTTOM;
30731         this.split.orientation = Roo.SplitBar.VERTICAL;
30732         this.split.el.addClass("x-layout-split-v");
30733     }
30734     var size = config.initialSize || config.height;
30735     if(typeof size != "undefined"){
30736         this.el.setHeight(size);
30737     }
30738 };
30739 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30740     orientation: Roo.SplitBar.VERTICAL,
30741     getBox : function(){
30742         if(this.collapsed){
30743             return this.collapsedEl.getBox();
30744         }
30745         var box = this.el.getBox();
30746         if(this.split){
30747             var sh = this.split.el.getHeight();
30748             box.height += sh;
30749             box.y -= sh;
30750         }
30751         return box;
30752     },
30753     
30754     updateBox : function(box){
30755         if(this.split && !this.collapsed){
30756             var sh = this.split.el.getHeight();
30757             box.height -= sh;
30758             box.y += sh;
30759             this.split.el.setLeft(box.x);
30760             this.split.el.setTop(box.y-sh);
30761             this.split.el.setWidth(box.width);
30762         }
30763         if(this.collapsed){
30764             this.updateBody(box.width, null);
30765         }
30766         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30767     }
30768 });
30769
30770 Roo.EastLayoutRegion = function(mgr, config){
30771     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30772     if(this.split){
30773         this.split.placement = Roo.SplitBar.RIGHT;
30774         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30775         this.split.el.addClass("x-layout-split-h");
30776     }
30777     var size = config.initialSize || config.width;
30778     if(typeof size != "undefined"){
30779         this.el.setWidth(size);
30780     }
30781 };
30782 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30783     orientation: Roo.SplitBar.HORIZONTAL,
30784     getBox : function(){
30785         if(this.collapsed){
30786             return this.collapsedEl.getBox();
30787         }
30788         var box = this.el.getBox();
30789         if(this.split){
30790             var sw = this.split.el.getWidth();
30791             box.width += sw;
30792             box.x -= sw;
30793         }
30794         return box;
30795     },
30796
30797     updateBox : function(box){
30798         if(this.split && !this.collapsed){
30799             var sw = this.split.el.getWidth();
30800             box.width -= sw;
30801             this.split.el.setLeft(box.x);
30802             this.split.el.setTop(box.y);
30803             this.split.el.setHeight(box.height);
30804             box.x += sw;
30805         }
30806         if(this.collapsed){
30807             this.updateBody(null, box.height);
30808         }
30809         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30810     }
30811 });
30812
30813 Roo.WestLayoutRegion = function(mgr, config){
30814     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30815     if(this.split){
30816         this.split.placement = Roo.SplitBar.LEFT;
30817         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30818         this.split.el.addClass("x-layout-split-h");
30819     }
30820     var size = config.initialSize || config.width;
30821     if(typeof size != "undefined"){
30822         this.el.setWidth(size);
30823     }
30824 };
30825 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30826     orientation: Roo.SplitBar.HORIZONTAL,
30827     getBox : function(){
30828         if(this.collapsed){
30829             return this.collapsedEl.getBox();
30830         }
30831         var box = this.el.getBox();
30832         if(this.split){
30833             box.width += this.split.el.getWidth();
30834         }
30835         return box;
30836     },
30837     
30838     updateBox : function(box){
30839         if(this.split && !this.collapsed){
30840             var sw = this.split.el.getWidth();
30841             box.width -= sw;
30842             this.split.el.setLeft(box.x+box.width);
30843             this.split.el.setTop(box.y);
30844             this.split.el.setHeight(box.height);
30845         }
30846         if(this.collapsed){
30847             this.updateBody(null, box.height);
30848         }
30849         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30850     }
30851 });
30852 /*
30853  * Based on:
30854  * Ext JS Library 1.1.1
30855  * Copyright(c) 2006-2007, Ext JS, LLC.
30856  *
30857  * Originally Released Under LGPL - original licence link has changed is not relivant.
30858  *
30859  * Fork - LGPL
30860  * <script type="text/javascript">
30861  */
30862  
30863  
30864 /*
30865  * Private internal class for reading and applying state
30866  */
30867 Roo.LayoutStateManager = function(layout){
30868      // default empty state
30869      this.state = {
30870         north: {},
30871         south: {},
30872         east: {},
30873         west: {}       
30874     };
30875 };
30876
30877 Roo.LayoutStateManager.prototype = {
30878     init : function(layout, provider){
30879         this.provider = provider;
30880         var state = provider.get(layout.id+"-layout-state");
30881         if(state){
30882             var wasUpdating = layout.isUpdating();
30883             if(!wasUpdating){
30884                 layout.beginUpdate();
30885             }
30886             for(var key in state){
30887                 if(typeof state[key] != "function"){
30888                     var rstate = state[key];
30889                     var r = layout.getRegion(key);
30890                     if(r && rstate){
30891                         if(rstate.size){
30892                             r.resizeTo(rstate.size);
30893                         }
30894                         if(rstate.collapsed == true){
30895                             r.collapse(true);
30896                         }else{
30897                             r.expand(null, true);
30898                         }
30899                     }
30900                 }
30901             }
30902             if(!wasUpdating){
30903                 layout.endUpdate();
30904             }
30905             this.state = state; 
30906         }
30907         this.layout = layout;
30908         layout.on("regionresized", this.onRegionResized, this);
30909         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30910         layout.on("regionexpanded", this.onRegionExpanded, this);
30911     },
30912     
30913     storeState : function(){
30914         this.provider.set(this.layout.id+"-layout-state", this.state);
30915     },
30916     
30917     onRegionResized : function(region, newSize){
30918         this.state[region.getPosition()].size = newSize;
30919         this.storeState();
30920     },
30921     
30922     onRegionCollapsed : function(region){
30923         this.state[region.getPosition()].collapsed = true;
30924         this.storeState();
30925     },
30926     
30927     onRegionExpanded : function(region){
30928         this.state[region.getPosition()].collapsed = false;
30929         this.storeState();
30930     }
30931 };/*
30932  * Based on:
30933  * Ext JS Library 1.1.1
30934  * Copyright(c) 2006-2007, Ext JS, LLC.
30935  *
30936  * Originally Released Under LGPL - original licence link has changed is not relivant.
30937  *
30938  * Fork - LGPL
30939  * <script type="text/javascript">
30940  */
30941 /**
30942  * @class Roo.ContentPanel
30943  * @extends Roo.util.Observable
30944  * A basic ContentPanel element.
30945  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30946  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30947  * @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
30948  * @cfg {Boolean} closable True if the panel can be closed/removed
30949  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
30950  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30951  * @cfg {Toolbar} toolbar A toolbar for this panel
30952  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
30953  * @cfg {String} title The title for this panel
30954  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30955  * @cfg {String} url Calls {@link #setUrl} with this value
30956  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30957  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
30958  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
30959  * @constructor
30960  * Create a new ContentPanel.
30961  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30962  * @param {String/Object} config A string to set only the title or a config object
30963  * @param {String} content (optional) Set the HTML content for this panel
30964  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30965  */
30966 Roo.ContentPanel = function(el, config, content){
30967     
30968      
30969     /*
30970     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30971         config = el;
30972         el = Roo.id();
30973     }
30974     if (config && config.parentLayout) { 
30975         el = config.parentLayout.el.createChild(); 
30976     }
30977     */
30978     if(el.autoCreate){ // xtype is available if this is called from factory
30979         config = el;
30980         el = Roo.id();
30981     }
30982     this.el = Roo.get(el);
30983     if(!this.el && config && config.autoCreate){
30984         if(typeof config.autoCreate == "object"){
30985             if(!config.autoCreate.id){
30986                 config.autoCreate.id = config.id||el;
30987             }
30988             this.el = Roo.DomHelper.append(document.body,
30989                         config.autoCreate, true);
30990         }else{
30991             this.el = Roo.DomHelper.append(document.body,
30992                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30993         }
30994     }
30995     this.closable = false;
30996     this.loaded = false;
30997     this.active = false;
30998     if(typeof config == "string"){
30999         this.title = config;
31000     }else{
31001         Roo.apply(this, config);
31002     }
31003     
31004     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31005         this.wrapEl = this.el.wrap();    
31006         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
31007         
31008     }
31009     
31010     
31011     
31012     if(this.resizeEl){
31013         this.resizeEl = Roo.get(this.resizeEl, true);
31014     }else{
31015         this.resizeEl = this.el;
31016     }
31017     this.addEvents({
31018         /**
31019          * @event activate
31020          * Fires when this panel is activated. 
31021          * @param {Roo.ContentPanel} this
31022          */
31023         "activate" : true,
31024         /**
31025          * @event deactivate
31026          * Fires when this panel is activated. 
31027          * @param {Roo.ContentPanel} this
31028          */
31029         "deactivate" : true,
31030
31031         /**
31032          * @event resize
31033          * Fires when this panel is resized if fitToFrame is true.
31034          * @param {Roo.ContentPanel} this
31035          * @param {Number} width The width after any component adjustments
31036          * @param {Number} height The height after any component adjustments
31037          */
31038         "resize" : true
31039     });
31040     if(this.autoScroll){
31041         this.resizeEl.setStyle("overflow", "auto");
31042     } else {
31043         // fix randome scrolling
31044         this.el.on('scroll', function() {
31045             this.scrollTo('top',0); 
31046         });
31047     }
31048     content = content || this.content;
31049     if(content){
31050         this.setContent(content);
31051     }
31052     if(config && config.url){
31053         this.setUrl(this.url, this.params, this.loadOnce);
31054     }
31055     
31056     
31057     
31058     Roo.ContentPanel.superclass.constructor.call(this);
31059 };
31060
31061 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31062     tabTip:'',
31063     setRegion : function(region){
31064         this.region = region;
31065         if(region){
31066            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31067         }else{
31068            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31069         } 
31070     },
31071     
31072     /**
31073      * Returns the toolbar for this Panel if one was configured. 
31074      * @return {Roo.Toolbar} 
31075      */
31076     getToolbar : function(){
31077         return this.toolbar;
31078     },
31079     
31080     setActiveState : function(active){
31081         this.active = active;
31082         if(!active){
31083             this.fireEvent("deactivate", this);
31084         }else{
31085             this.fireEvent("activate", this);
31086         }
31087     },
31088     /**
31089      * Updates this panel's element
31090      * @param {String} content The new content
31091      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31092     */
31093     setContent : function(content, loadScripts){
31094         this.el.update(content, loadScripts);
31095     },
31096
31097     ignoreResize : function(w, h){
31098         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31099             return true;
31100         }else{
31101             this.lastSize = {width: w, height: h};
31102             return false;
31103         }
31104     },
31105     /**
31106      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31107      * @return {Roo.UpdateManager} The UpdateManager
31108      */
31109     getUpdateManager : function(){
31110         return this.el.getUpdateManager();
31111     },
31112      /**
31113      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31114      * @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:
31115 <pre><code>
31116 panel.load({
31117     url: "your-url.php",
31118     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31119     callback: yourFunction,
31120     scope: yourObject, //(optional scope)
31121     discardUrl: false,
31122     nocache: false,
31123     text: "Loading...",
31124     timeout: 30,
31125     scripts: false
31126 });
31127 </code></pre>
31128      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31129      * 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.
31130      * @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}
31131      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31132      * @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.
31133      * @return {Roo.ContentPanel} this
31134      */
31135     load : function(){
31136         var um = this.el.getUpdateManager();
31137         um.update.apply(um, arguments);
31138         return this;
31139     },
31140
31141
31142     /**
31143      * 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.
31144      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31145      * @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)
31146      * @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)
31147      * @return {Roo.UpdateManager} The UpdateManager
31148      */
31149     setUrl : function(url, params, loadOnce){
31150         if(this.refreshDelegate){
31151             this.removeListener("activate", this.refreshDelegate);
31152         }
31153         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31154         this.on("activate", this.refreshDelegate);
31155         return this.el.getUpdateManager();
31156     },
31157     
31158     _handleRefresh : function(url, params, loadOnce){
31159         if(!loadOnce || !this.loaded){
31160             var updater = this.el.getUpdateManager();
31161             updater.update(url, params, this._setLoaded.createDelegate(this));
31162         }
31163     },
31164     
31165     _setLoaded : function(){
31166         this.loaded = true;
31167     }, 
31168     
31169     /**
31170      * Returns this panel's id
31171      * @return {String} 
31172      */
31173     getId : function(){
31174         return this.el.id;
31175     },
31176     
31177     /** 
31178      * Returns this panel's element - used by regiosn to add.
31179      * @return {Roo.Element} 
31180      */
31181     getEl : function(){
31182         return this.wrapEl || this.el;
31183     },
31184     
31185     adjustForComponents : function(width, height){
31186         if(this.resizeEl != this.el){
31187             width -= this.el.getFrameWidth('lr');
31188             height -= this.el.getFrameWidth('tb');
31189         }
31190         if(this.toolbar){
31191             var te = this.toolbar.getEl();
31192             height -= te.getHeight();
31193             te.setWidth(width);
31194         }
31195         if(this.adjustments){
31196             width += this.adjustments[0];
31197             height += this.adjustments[1];
31198         }
31199         return {"width": width, "height": height};
31200     },
31201     
31202     setSize : function(width, height){
31203         if(this.fitToFrame && !this.ignoreResize(width, height)){
31204             if(this.fitContainer && this.resizeEl != this.el){
31205                 this.el.setSize(width, height);
31206             }
31207             var size = this.adjustForComponents(width, height);
31208             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31209             this.fireEvent('resize', this, size.width, size.height);
31210         }
31211     },
31212     
31213     /**
31214      * Returns this panel's title
31215      * @return {String} 
31216      */
31217     getTitle : function(){
31218         return this.title;
31219     },
31220     
31221     /**
31222      * Set this panel's title
31223      * @param {String} title
31224      */
31225     setTitle : function(title){
31226         this.title = title;
31227         if(this.region){
31228             this.region.updatePanelTitle(this, title);
31229         }
31230     },
31231     
31232     /**
31233      * Returns true is this panel was configured to be closable
31234      * @return {Boolean} 
31235      */
31236     isClosable : function(){
31237         return this.closable;
31238     },
31239     
31240     beforeSlide : function(){
31241         this.el.clip();
31242         this.resizeEl.clip();
31243     },
31244     
31245     afterSlide : function(){
31246         this.el.unclip();
31247         this.resizeEl.unclip();
31248     },
31249     
31250     /**
31251      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31252      *   Will fail silently if the {@link #setUrl} method has not been called.
31253      *   This does not activate the panel, just updates its content.
31254      */
31255     refresh : function(){
31256         if(this.refreshDelegate){
31257            this.loaded = false;
31258            this.refreshDelegate();
31259         }
31260     },
31261     
31262     /**
31263      * Destroys this panel
31264      */
31265     destroy : function(){
31266         this.el.removeAllListeners();
31267         var tempEl = document.createElement("span");
31268         tempEl.appendChild(this.el.dom);
31269         tempEl.innerHTML = "";
31270         this.el.remove();
31271         this.el = null;
31272     },
31273     
31274       /**
31275      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31276      * <pre><code>
31277
31278 layout.addxtype({
31279        xtype : 'Form',
31280        items: [ .... ]
31281    }
31282 );
31283
31284 </code></pre>
31285      * @param {Object} cfg Xtype definition of item to add.
31286      */
31287     
31288     addxtype : function(cfg) {
31289         // add form..
31290         if (cfg.xtype.match(/^Form$/)) {
31291             var el = this.el.createChild();
31292
31293             this.form = new  Roo.form.Form(cfg);
31294             
31295             
31296             if ( this.form.allItems.length) this.form.render(el.dom);
31297             return this.form;
31298         }
31299         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
31300             // views..
31301             cfg.el = this.el.appendChild(document.createElement("div"));
31302             // factory?
31303             var ret = new Roo[cfg.xtype](cfg);
31304             ret.render(false, ''); // render blank..
31305             return ret;
31306             
31307         }
31308         return false;
31309         
31310     }
31311 });
31312
31313 /**
31314  * @class Roo.GridPanel
31315  * @extends Roo.ContentPanel
31316  * @constructor
31317  * Create a new GridPanel.
31318  * @param {Roo.grid.Grid} grid The grid for this panel
31319  * @param {String/Object} config A string to set only the panel's title, or a config object
31320  */
31321 Roo.GridPanel = function(grid, config){
31322     
31323   
31324     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31325         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31326         
31327     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31328     
31329     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31330     
31331     if(this.toolbar){
31332         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31333     }
31334     // xtype created footer. - not sure if will work as we normally have to render first..
31335     if (this.footer && !this.footer.el && this.footer.xtype) {
31336         
31337         this.footer.container = this.grid.getView().getFooterPanel(true);
31338         this.footer.dataSource = this.grid.dataSource;
31339         this.footer = Roo.factory(this.footer, Roo);
31340         
31341     }
31342     
31343     grid.monitorWindowResize = false; // turn off autosizing
31344     grid.autoHeight = false;
31345     grid.autoWidth = false;
31346     this.grid = grid;
31347     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31348 };
31349
31350 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31351     getId : function(){
31352         return this.grid.id;
31353     },
31354     
31355     /**
31356      * Returns the grid for this panel
31357      * @return {Roo.grid.Grid} 
31358      */
31359     getGrid : function(){
31360         return this.grid;    
31361     },
31362     
31363     setSize : function(width, height){
31364         if(!this.ignoreResize(width, height)){
31365             var grid = this.grid;
31366             var size = this.adjustForComponents(width, height);
31367             grid.getGridEl().setSize(size.width, size.height);
31368             grid.autoSize();
31369         }
31370     },
31371     
31372     beforeSlide : function(){
31373         this.grid.getView().scroller.clip();
31374     },
31375     
31376     afterSlide : function(){
31377         this.grid.getView().scroller.unclip();
31378     },
31379     
31380     destroy : function(){
31381         this.grid.destroy();
31382         delete this.grid;
31383         Roo.GridPanel.superclass.destroy.call(this); 
31384     }
31385 });
31386
31387
31388 /**
31389  * @class Roo.NestedLayoutPanel
31390  * @extends Roo.ContentPanel
31391  * @constructor
31392  * Create a new NestedLayoutPanel.
31393  * 
31394  * 
31395  * @param {Roo.BorderLayout} layout The layout for this panel
31396  * @param {String/Object} config A string to set only the title or a config object
31397  */
31398 Roo.NestedLayoutPanel = function(layout, config)
31399 {
31400     // construct with only one argument..
31401     /* FIXME - implement nicer consturctors
31402     if (layout.layout) {
31403         config = layout;
31404         layout = config.layout;
31405         delete config.layout;
31406     }
31407     if (layout.xtype && !layout.getEl) {
31408         // then layout needs constructing..
31409         layout = Roo.factory(layout, Roo);
31410     }
31411     */
31412     
31413     
31414     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31415     
31416     layout.monitorWindowResize = false; // turn off autosizing
31417     this.layout = layout;
31418     this.layout.getEl().addClass("x-layout-nested-layout");
31419     
31420     
31421     
31422     
31423 };
31424
31425 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31426
31427     setSize : function(width, height){
31428         if(!this.ignoreResize(width, height)){
31429             var size = this.adjustForComponents(width, height);
31430             var el = this.layout.getEl();
31431             el.setSize(size.width, size.height);
31432             var touch = el.dom.offsetWidth;
31433             this.layout.layout();
31434             // ie requires a double layout on the first pass
31435             if(Roo.isIE && !this.initialized){
31436                 this.initialized = true;
31437                 this.layout.layout();
31438             }
31439         }
31440     },
31441     
31442     // activate all subpanels if not currently active..
31443     
31444     setActiveState : function(active){
31445         this.active = active;
31446         if(!active){
31447             this.fireEvent("deactivate", this);
31448             return;
31449         }
31450         
31451         this.fireEvent("activate", this);
31452         // not sure if this should happen before or after..
31453         if (!this.layout) {
31454             return; // should not happen..
31455         }
31456         var reg = false;
31457         for (var r in this.layout.regions) {
31458             reg = this.layout.getRegion(r);
31459             if (reg.getActivePanel()) {
31460                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31461                 reg.setActivePanel(reg.getActivePanel());
31462                 continue;
31463             }
31464             if (!reg.panels.length) {
31465                 continue;
31466             }
31467             reg.showPanel(reg.getPanel(0));
31468         }
31469         
31470         
31471         
31472         
31473     },
31474     
31475     /**
31476      * Returns the nested BorderLayout for this panel
31477      * @return {Roo.BorderLayout} 
31478      */
31479     getLayout : function(){
31480         return this.layout;
31481     },
31482     
31483      /**
31484      * Adds a xtype elements to the layout of the nested panel
31485      * <pre><code>
31486
31487 panel.addxtype({
31488        xtype : 'ContentPanel',
31489        region: 'west',
31490        items: [ .... ]
31491    }
31492 );
31493
31494 panel.addxtype({
31495         xtype : 'NestedLayoutPanel',
31496         region: 'west',
31497         layout: {
31498            center: { },
31499            west: { }   
31500         },
31501         items : [ ... list of content panels or nested layout panels.. ]
31502    }
31503 );
31504 </code></pre>
31505      * @param {Object} cfg Xtype definition of item to add.
31506      */
31507     addxtype : function(cfg) {
31508         return this.layout.addxtype(cfg);
31509     
31510     }
31511 });
31512
31513 Roo.ScrollPanel = function(el, config, content){
31514     config = config || {};
31515     config.fitToFrame = true;
31516     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31517     
31518     this.el.dom.style.overflow = "hidden";
31519     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31520     this.el.removeClass("x-layout-inactive-content");
31521     this.el.on("mousewheel", this.onWheel, this);
31522
31523     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31524     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31525     up.unselectable(); down.unselectable();
31526     up.on("click", this.scrollUp, this);
31527     down.on("click", this.scrollDown, this);
31528     up.addClassOnOver("x-scroller-btn-over");
31529     down.addClassOnOver("x-scroller-btn-over");
31530     up.addClassOnClick("x-scroller-btn-click");
31531     down.addClassOnClick("x-scroller-btn-click");
31532     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31533
31534     this.resizeEl = this.el;
31535     this.el = wrap; this.up = up; this.down = down;
31536 };
31537
31538 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31539     increment : 100,
31540     wheelIncrement : 5,
31541     scrollUp : function(){
31542         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31543     },
31544
31545     scrollDown : function(){
31546         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31547     },
31548
31549     afterScroll : function(){
31550         var el = this.resizeEl;
31551         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31552         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31553         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31554     },
31555
31556     setSize : function(){
31557         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31558         this.afterScroll();
31559     },
31560
31561     onWheel : function(e){
31562         var d = e.getWheelDelta();
31563         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31564         this.afterScroll();
31565         e.stopEvent();
31566     },
31567
31568     setContent : function(content, loadScripts){
31569         this.resizeEl.update(content, loadScripts);
31570     }
31571
31572 });
31573
31574
31575
31576
31577
31578
31579
31580
31581
31582 /**
31583  * @class Roo.TreePanel
31584  * @extends Roo.ContentPanel
31585  * @constructor
31586  * Create a new TreePanel. - defaults to fit/scoll contents.
31587  * @param {String/Object} config A string to set only the panel's title, or a config object
31588  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31589  */
31590 Roo.TreePanel = function(config){
31591     var el = config.el;
31592     var tree = config.tree;
31593     delete config.tree; 
31594     delete config.el; // hopefull!
31595     
31596     // wrapper for IE7 strict & safari scroll issue
31597     
31598     var treeEl = el.createChild();
31599     config.resizeEl = treeEl;
31600     
31601     
31602     
31603     Roo.TreePanel.superclass.constructor.call(this, el, config);
31604  
31605  
31606     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31607     //console.log(tree);
31608     this.on('activate', function()
31609     {
31610         if (this.tree.rendered) {
31611             return;
31612         }
31613         //console.log('render tree');
31614         this.tree.render();
31615     });
31616     
31617     this.on('resize',  function (cp, w, h) {
31618             this.tree.innerCt.setWidth(w);
31619             this.tree.innerCt.setHeight(h);
31620             this.tree.innerCt.setStyle('overflow-y', 'auto');
31621     });
31622
31623         
31624     
31625 };
31626
31627 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31628     fitToFrame : true,
31629     autoScroll : true
31630 });
31631
31632
31633
31634
31635
31636
31637
31638
31639
31640
31641
31642 /*
31643  * Based on:
31644  * Ext JS Library 1.1.1
31645  * Copyright(c) 2006-2007, Ext JS, LLC.
31646  *
31647  * Originally Released Under LGPL - original licence link has changed is not relivant.
31648  *
31649  * Fork - LGPL
31650  * <script type="text/javascript">
31651  */
31652  
31653
31654 /**
31655  * @class Roo.ReaderLayout
31656  * @extends Roo.BorderLayout
31657  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31658  * center region containing two nested regions (a top one for a list view and one for item preview below),
31659  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31660  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31661  * expedites the setup of the overall layout and regions for this common application style.
31662  * Example:
31663  <pre><code>
31664 var reader = new Roo.ReaderLayout();
31665 var CP = Roo.ContentPanel;  // shortcut for adding
31666
31667 reader.beginUpdate();
31668 reader.add("north", new CP("north", "North"));
31669 reader.add("west", new CP("west", {title: "West"}));
31670 reader.add("east", new CP("east", {title: "East"}));
31671
31672 reader.regions.listView.add(new CP("listView", "List"));
31673 reader.regions.preview.add(new CP("preview", "Preview"));
31674 reader.endUpdate();
31675 </code></pre>
31676 * @constructor
31677 * Create a new ReaderLayout
31678 * @param {Object} config Configuration options
31679 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31680 * document.body if omitted)
31681 */
31682 Roo.ReaderLayout = function(config, renderTo){
31683     var c = config || {size:{}};
31684     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31685         north: c.north !== false ? Roo.apply({
31686             split:false,
31687             initialSize: 32,
31688             titlebar: false
31689         }, c.north) : false,
31690         west: c.west !== false ? Roo.apply({
31691             split:true,
31692             initialSize: 200,
31693             minSize: 175,
31694             maxSize: 400,
31695             titlebar: true,
31696             collapsible: true,
31697             animate: true,
31698             margins:{left:5,right:0,bottom:5,top:5},
31699             cmargins:{left:5,right:5,bottom:5,top:5}
31700         }, c.west) : false,
31701         east: c.east !== false ? Roo.apply({
31702             split:true,
31703             initialSize: 200,
31704             minSize: 175,
31705             maxSize: 400,
31706             titlebar: true,
31707             collapsible: true,
31708             animate: true,
31709             margins:{left:0,right:5,bottom:5,top:5},
31710             cmargins:{left:5,right:5,bottom:5,top:5}
31711         }, c.east) : false,
31712         center: Roo.apply({
31713             tabPosition: 'top',
31714             autoScroll:false,
31715             closeOnTab: true,
31716             titlebar:false,
31717             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31718         }, c.center)
31719     });
31720
31721     this.el.addClass('x-reader');
31722
31723     this.beginUpdate();
31724
31725     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31726         south: c.preview !== false ? Roo.apply({
31727             split:true,
31728             initialSize: 200,
31729             minSize: 100,
31730             autoScroll:true,
31731             collapsible:true,
31732             titlebar: true,
31733             cmargins:{top:5,left:0, right:0, bottom:0}
31734         }, c.preview) : false,
31735         center: Roo.apply({
31736             autoScroll:false,
31737             titlebar:false,
31738             minHeight:200
31739         }, c.listView)
31740     });
31741     this.add('center', new Roo.NestedLayoutPanel(inner,
31742             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31743
31744     this.endUpdate();
31745
31746     this.regions.preview = inner.getRegion('south');
31747     this.regions.listView = inner.getRegion('center');
31748 };
31749
31750 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31751  * Based on:
31752  * Ext JS Library 1.1.1
31753  * Copyright(c) 2006-2007, Ext JS, LLC.
31754  *
31755  * Originally Released Under LGPL - original licence link has changed is not relivant.
31756  *
31757  * Fork - LGPL
31758  * <script type="text/javascript">
31759  */
31760  
31761 /**
31762  * @class Roo.grid.Grid
31763  * @extends Roo.util.Observable
31764  * This class represents the primary interface of a component based grid control.
31765  * <br><br>Usage:<pre><code>
31766  var grid = new Roo.grid.Grid("my-container-id", {
31767      ds: myDataStore,
31768      cm: myColModel,
31769      selModel: mySelectionModel,
31770      autoSizeColumns: true,
31771      monitorWindowResize: false,
31772      trackMouseOver: true
31773  });
31774  // set any options
31775  grid.render();
31776  * </code></pre>
31777  * <b>Common Problems:</b><br/>
31778  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31779  * element will correct this<br/>
31780  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31781  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31782  * are unpredictable.<br/>
31783  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31784  * grid to calculate dimensions/offsets.<br/>
31785   * @constructor
31786  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31787  * The container MUST have some type of size defined for the grid to fill. The container will be
31788  * automatically set to position relative if it isn't already.
31789  * @param {Object} config A config object that sets properties on this grid.
31790  */
31791 Roo.grid.Grid = function(container, config){
31792         // initialize the container
31793         this.container = Roo.get(container);
31794         this.container.update("");
31795         this.container.setStyle("overflow", "hidden");
31796     this.container.addClass('x-grid-container');
31797
31798     this.id = this.container.id;
31799
31800     Roo.apply(this, config);
31801     // check and correct shorthanded configs
31802     if(this.ds){
31803         this.dataSource = this.ds;
31804         delete this.ds;
31805     }
31806     if(this.cm){
31807         this.colModel = this.cm;
31808         delete this.cm;
31809     }
31810     if(this.sm){
31811         this.selModel = this.sm;
31812         delete this.sm;
31813     }
31814
31815     if (this.selModel) {
31816         this.selModel = Roo.factory(this.selModel, Roo.grid);
31817         this.sm = this.selModel;
31818         this.sm.xmodule = this.xmodule || false;
31819     }
31820     if (typeof(this.colModel.config) == 'undefined') {
31821         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31822         this.cm = this.colModel;
31823         this.cm.xmodule = this.xmodule || false;
31824     }
31825     if (this.dataSource) {
31826         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31827         this.ds = this.dataSource;
31828         this.ds.xmodule = this.xmodule || false;
31829         
31830     }
31831     
31832     
31833     
31834     if(this.width){
31835         this.container.setWidth(this.width);
31836     }
31837
31838     if(this.height){
31839         this.container.setHeight(this.height);
31840     }
31841     /** @private */
31842         this.addEvents({
31843         // raw events
31844         /**
31845          * @event click
31846          * The raw click event for the entire grid.
31847          * @param {Roo.EventObject} e
31848          */
31849         "click" : true,
31850         /**
31851          * @event dblclick
31852          * The raw dblclick event for the entire grid.
31853          * @param {Roo.EventObject} e
31854          */
31855         "dblclick" : true,
31856         /**
31857          * @event contextmenu
31858          * The raw contextmenu event for the entire grid.
31859          * @param {Roo.EventObject} e
31860          */
31861         "contextmenu" : true,
31862         /**
31863          * @event mousedown
31864          * The raw mousedown event for the entire grid.
31865          * @param {Roo.EventObject} e
31866          */
31867         "mousedown" : true,
31868         /**
31869          * @event mouseup
31870          * The raw mouseup event for the entire grid.
31871          * @param {Roo.EventObject} e
31872          */
31873         "mouseup" : true,
31874         /**
31875          * @event mouseover
31876          * The raw mouseover event for the entire grid.
31877          * @param {Roo.EventObject} e
31878          */
31879         "mouseover" : true,
31880         /**
31881          * @event mouseout
31882          * The raw mouseout event for the entire grid.
31883          * @param {Roo.EventObject} e
31884          */
31885         "mouseout" : true,
31886         /**
31887          * @event keypress
31888          * The raw keypress event for the entire grid.
31889          * @param {Roo.EventObject} e
31890          */
31891         "keypress" : true,
31892         /**
31893          * @event keydown
31894          * The raw keydown event for the entire grid.
31895          * @param {Roo.EventObject} e
31896          */
31897         "keydown" : true,
31898
31899         // custom events
31900
31901         /**
31902          * @event cellclick
31903          * Fires when a cell is clicked
31904          * @param {Grid} this
31905          * @param {Number} rowIndex
31906          * @param {Number} columnIndex
31907          * @param {Roo.EventObject} e
31908          */
31909         "cellclick" : true,
31910         /**
31911          * @event celldblclick
31912          * Fires when a cell is double clicked
31913          * @param {Grid} this
31914          * @param {Number} rowIndex
31915          * @param {Number} columnIndex
31916          * @param {Roo.EventObject} e
31917          */
31918         "celldblclick" : true,
31919         /**
31920          * @event rowclick
31921          * Fires when a row is clicked
31922          * @param {Grid} this
31923          * @param {Number} rowIndex
31924          * @param {Roo.EventObject} e
31925          */
31926         "rowclick" : true,
31927         /**
31928          * @event rowdblclick
31929          * Fires when a row is double clicked
31930          * @param {Grid} this
31931          * @param {Number} rowIndex
31932          * @param {Roo.EventObject} e
31933          */
31934         "rowdblclick" : true,
31935         /**
31936          * @event headerclick
31937          * Fires when a header is clicked
31938          * @param {Grid} this
31939          * @param {Number} columnIndex
31940          * @param {Roo.EventObject} e
31941          */
31942         "headerclick" : true,
31943         /**
31944          * @event headerdblclick
31945          * Fires when a header cell is double clicked
31946          * @param {Grid} this
31947          * @param {Number} columnIndex
31948          * @param {Roo.EventObject} e
31949          */
31950         "headerdblclick" : true,
31951         /**
31952          * @event rowcontextmenu
31953          * Fires when a row is right clicked
31954          * @param {Grid} this
31955          * @param {Number} rowIndex
31956          * @param {Roo.EventObject} e
31957          */
31958         "rowcontextmenu" : true,
31959         /**
31960          * @event cellcontextmenu
31961          * Fires when a cell is right clicked
31962          * @param {Grid} this
31963          * @param {Number} rowIndex
31964          * @param {Number} cellIndex
31965          * @param {Roo.EventObject} e
31966          */
31967          "cellcontextmenu" : true,
31968         /**
31969          * @event headercontextmenu
31970          * Fires when a header is right clicked
31971          * @param {Grid} this
31972          * @param {Number} columnIndex
31973          * @param {Roo.EventObject} e
31974          */
31975         "headercontextmenu" : true,
31976         /**
31977          * @event bodyscroll
31978          * Fires when the body element is scrolled
31979          * @param {Number} scrollLeft
31980          * @param {Number} scrollTop
31981          */
31982         "bodyscroll" : true,
31983         /**
31984          * @event columnresize
31985          * Fires when the user resizes a column
31986          * @param {Number} columnIndex
31987          * @param {Number} newSize
31988          */
31989         "columnresize" : true,
31990         /**
31991          * @event columnmove
31992          * Fires when the user moves a column
31993          * @param {Number} oldIndex
31994          * @param {Number} newIndex
31995          */
31996         "columnmove" : true,
31997         /**
31998          * @event startdrag
31999          * Fires when row(s) start being dragged
32000          * @param {Grid} this
32001          * @param {Roo.GridDD} dd The drag drop object
32002          * @param {event} e The raw browser event
32003          */
32004         "startdrag" : true,
32005         /**
32006          * @event enddrag
32007          * Fires when a drag operation is complete
32008          * @param {Grid} this
32009          * @param {Roo.GridDD} dd The drag drop object
32010          * @param {event} e The raw browser event
32011          */
32012         "enddrag" : true,
32013         /**
32014          * @event dragdrop
32015          * Fires when dragged row(s) are dropped on a valid DD target
32016          * @param {Grid} this
32017          * @param {Roo.GridDD} dd The drag drop object
32018          * @param {String} targetId The target drag drop object
32019          * @param {event} e The raw browser event
32020          */
32021         "dragdrop" : true,
32022         /**
32023          * @event dragover
32024          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32025          * @param {Grid} this
32026          * @param {Roo.GridDD} dd The drag drop object
32027          * @param {String} targetId The target drag drop object
32028          * @param {event} e The raw browser event
32029          */
32030         "dragover" : true,
32031         /**
32032          * @event dragenter
32033          *  Fires when the dragged row(s) first cross another DD target while being dragged
32034          * @param {Grid} this
32035          * @param {Roo.GridDD} dd The drag drop object
32036          * @param {String} targetId The target drag drop object
32037          * @param {event} e The raw browser event
32038          */
32039         "dragenter" : true,
32040         /**
32041          * @event dragout
32042          * Fires when the dragged row(s) leave another DD target while being dragged
32043          * @param {Grid} this
32044          * @param {Roo.GridDD} dd The drag drop object
32045          * @param {String} targetId The target drag drop object
32046          * @param {event} e The raw browser event
32047          */
32048         "dragout" : true,
32049         /**
32050          * @event rowclass
32051          * Fires when a row is rendered, so you can change add a style to it.
32052          * @param {GridView} gridview The grid view
32053          * @param {Object} rowcfg contains record, rowIndex and rowClass - set rowClass to add a style.
32054          */
32055         'rowclass' : true,
32056
32057         /**
32058          * @event render
32059          * Fires when the grid is rendered
32060          * @param {Grid} grid
32061          */
32062         'render' : true
32063     });
32064
32065     Roo.grid.Grid.superclass.constructor.call(this);
32066 };
32067 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32068     
32069     /**
32070      * @cfg {String} ddGroup - drag drop group.
32071          */
32072     
32073     /**
32074      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32075          */
32076         minColumnWidth : 25,
32077
32078     /**
32079          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32080          * <b>on initial render.</b> It is more efficient to explicitly size the columns
32081          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32082          */
32083         autoSizeColumns : false,
32084
32085         /**
32086          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32087          */
32088         autoSizeHeaders : true,
32089
32090         /**
32091          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32092          */
32093         monitorWindowResize : true,
32094
32095         /**
32096          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32097          * rows measured to get a columns size. Default is 0 (all rows).
32098          */
32099         maxRowsToMeasure : 0,
32100
32101         /**
32102          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32103          */
32104         trackMouseOver : true,
32105
32106     /**
32107          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32108          */
32109     
32110         /**
32111          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32112          */
32113         enableDragDrop : false,
32114
32115         /**
32116          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32117          */
32118         enableColumnMove : true,
32119
32120         /**
32121          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32122          */
32123         enableColumnHide : true,
32124
32125         /**
32126          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32127          */
32128         enableRowHeightSync : false,
32129
32130         /**
32131          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32132          */
32133         stripeRows : true,
32134
32135         /**
32136          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32137          */
32138         autoHeight : false,
32139
32140     /**
32141      * @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.
32142      */
32143     autoExpandColumn : false,
32144
32145     /**
32146     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32147     * Default is 50.
32148     */
32149     autoExpandMin : 50,
32150
32151     /**
32152     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32153     */
32154     autoExpandMax : 1000,
32155
32156     /**
32157          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32158          */
32159         view : null,
32160
32161         /**
32162      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32163          */
32164         loadMask : false,
32165     /**
32166      * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
32167          */
32168         dropTarget: false,
32169     // private
32170     rendered : false,
32171
32172     /**
32173     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32174     * of a fixed width. Default is false.
32175     */
32176     /**
32177     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32178     */
32179     /**
32180      * Called once after all setup has been completed and the grid is ready to be rendered.
32181      * @return {Roo.grid.Grid} this
32182      */
32183     render : function(){
32184         var c = this.container;
32185         // try to detect autoHeight/width mode
32186         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32187             this.autoHeight = true;
32188         }
32189         var view = this.getView();
32190         view.init(this);
32191
32192         c.on("click", this.onClick, this);
32193         c.on("dblclick", this.onDblClick, this);
32194         c.on("contextmenu", this.onContextMenu, this);
32195         c.on("keydown", this.onKeyDown, this);
32196
32197         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32198
32199         this.getSelectionModel().init(this);
32200
32201         view.render();
32202
32203         if(this.loadMask){
32204             this.loadMask = new Roo.LoadMask(this.container,
32205                     Roo.apply({store:this.dataSource}, this.loadMask));
32206         }
32207         
32208         
32209         if (this.toolbar && this.toolbar.xtype) {
32210             this.toolbar.container = this.getView().getHeaderPanel(true);
32211             this.toolbar = new Ext.Toolbar(this.toolbar);
32212         }
32213         if (this.footer && this.footer.xtype) {
32214             this.footer.dataSource = this.getDataSource();
32215             this.footer.container = this.getView().getFooterPanel(true);
32216             this.footer = Roo.factory(this.footer, Roo);
32217         }
32218         if (this.dropTarget && this.dropTarget.xtype) {
32219             delete this.dropTarget.xtype;
32220             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32221         }
32222         
32223         
32224         this.rendered = true;
32225         this.fireEvent('render', this);
32226         return this;
32227     },
32228
32229         /**
32230          * Reconfigures the grid to use a different Store and Column Model.
32231          * The View will be bound to the new objects and refreshed.
32232          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32233          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32234          */
32235     reconfigure : function(dataSource, colModel){
32236         if(this.loadMask){
32237             this.loadMask.destroy();
32238             this.loadMask = new Roo.LoadMask(this.container,
32239                     Roo.apply({store:dataSource}, this.loadMask));
32240         }
32241         this.view.bind(dataSource, colModel);
32242         this.dataSource = dataSource;
32243         this.colModel = colModel;
32244         this.view.refresh(true);
32245     },
32246
32247     // private
32248     onKeyDown : function(e){
32249         this.fireEvent("keydown", e);
32250     },
32251
32252     /**
32253      * Destroy this grid.
32254      * @param {Boolean} removeEl True to remove the element
32255      */
32256     destroy : function(removeEl, keepListeners){
32257         if(this.loadMask){
32258             this.loadMask.destroy();
32259         }
32260         var c = this.container;
32261         c.removeAllListeners();
32262         this.view.destroy();
32263         this.colModel.purgeListeners();
32264         if(!keepListeners){
32265             this.purgeListeners();
32266         }
32267         c.update("");
32268         if(removeEl === true){
32269             c.remove();
32270         }
32271     },
32272
32273     // private
32274     processEvent : function(name, e){
32275         this.fireEvent(name, e);
32276         var t = e.getTarget();
32277         var v = this.view;
32278         var header = v.findHeaderIndex(t);
32279         if(header !== false){
32280             this.fireEvent("header" + name, this, header, e);
32281         }else{
32282             var row = v.findRowIndex(t);
32283             var cell = v.findCellIndex(t);
32284             if(row !== false){
32285                 this.fireEvent("row" + name, this, row, e);
32286                 if(cell !== false){
32287                     this.fireEvent("cell" + name, this, row, cell, e);
32288                 }
32289             }
32290         }
32291     },
32292
32293     // private
32294     onClick : function(e){
32295         this.processEvent("click", e);
32296     },
32297
32298     // private
32299     onContextMenu : function(e, t){
32300         this.processEvent("contextmenu", e);
32301     },
32302
32303     // private
32304     onDblClick : function(e){
32305         this.processEvent("dblclick", e);
32306     },
32307
32308     // private
32309     walkCells : function(row, col, step, fn, scope){
32310         var cm = this.colModel, clen = cm.getColumnCount();
32311         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32312         if(step < 0){
32313             if(col < 0){
32314                 row--;
32315                 first = false;
32316             }
32317             while(row >= 0){
32318                 if(!first){
32319                     col = clen-1;
32320                 }
32321                 first = false;
32322                 while(col >= 0){
32323                     if(fn.call(scope || this, row, col, cm) === true){
32324                         return [row, col];
32325                     }
32326                     col--;
32327                 }
32328                 row--;
32329             }
32330         } else {
32331             if(col >= clen){
32332                 row++;
32333                 first = false;
32334             }
32335             while(row < rlen){
32336                 if(!first){
32337                     col = 0;
32338                 }
32339                 first = false;
32340                 while(col < clen){
32341                     if(fn.call(scope || this, row, col, cm) === true){
32342                         return [row, col];
32343                     }
32344                     col++;
32345                 }
32346                 row++;
32347             }
32348         }
32349         return null;
32350     },
32351
32352     // private
32353     getSelections : function(){
32354         return this.selModel.getSelections();
32355     },
32356
32357     /**
32358      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32359      * but if manual update is required this method will initiate it.
32360      */
32361     autoSize : function(){
32362         if(this.rendered){
32363             this.view.layout();
32364             if(this.view.adjustForScroll){
32365                 this.view.adjustForScroll();
32366             }
32367         }
32368     },
32369
32370     /**
32371      * Returns the grid's underlying element.
32372      * @return {Element} The element
32373      */
32374     getGridEl : function(){
32375         return this.container;
32376     },
32377
32378     // private for compatibility, overridden by editor grid
32379     stopEditing : function(){},
32380
32381     /**
32382      * Returns the grid's SelectionModel.
32383      * @return {SelectionModel}
32384      */
32385     getSelectionModel : function(){
32386         if(!this.selModel){
32387             this.selModel = new Roo.grid.RowSelectionModel();
32388         }
32389         return this.selModel;
32390     },
32391
32392     /**
32393      * Returns the grid's DataSource.
32394      * @return {DataSource}
32395      */
32396     getDataSource : function(){
32397         return this.dataSource;
32398     },
32399
32400     /**
32401      * Returns the grid's ColumnModel.
32402      * @return {ColumnModel}
32403      */
32404     getColumnModel : function(){
32405         return this.colModel;
32406     },
32407
32408     /**
32409      * Returns the grid's GridView object.
32410      * @return {GridView}
32411      */
32412     getView : function(){
32413         if(!this.view){
32414             this.view = new Roo.grid.GridView(this.viewConfig);
32415         }
32416         return this.view;
32417     },
32418     /**
32419      * Called to get grid's drag proxy text, by default returns this.ddText.
32420      * @return {String}
32421      */
32422     getDragDropText : function(){
32423         var count = this.selModel.getCount();
32424         return String.format(this.ddText, count, count == 1 ? '' : 's');
32425     }
32426 });
32427 /**
32428  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32429  * %0 is replaced with the number of selected rows.
32430  * @type String
32431  */
32432 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32433  * Based on:
32434  * Ext JS Library 1.1.1
32435  * Copyright(c) 2006-2007, Ext JS, LLC.
32436  *
32437  * Originally Released Under LGPL - original licence link has changed is not relivant.
32438  *
32439  * Fork - LGPL
32440  * <script type="text/javascript">
32441  */
32442  
32443 Roo.grid.AbstractGridView = function(){
32444         this.grid = null;
32445         
32446         this.events = {
32447             "beforerowremoved" : true,
32448             "beforerowsinserted" : true,
32449             "beforerefresh" : true,
32450             "rowremoved" : true,
32451             "rowsinserted" : true,
32452             "rowupdated" : true,
32453             "refresh" : true
32454         };
32455     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32456 };
32457
32458 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32459     rowClass : "x-grid-row",
32460     cellClass : "x-grid-cell",
32461     tdClass : "x-grid-td",
32462     hdClass : "x-grid-hd",
32463     splitClass : "x-grid-hd-split",
32464     
32465         init: function(grid){
32466         this.grid = grid;
32467                 var cid = this.grid.getGridEl().id;
32468         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32469         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32470         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32471         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32472         },
32473         
32474         getColumnRenderers : function(){
32475         var renderers = [];
32476         var cm = this.grid.colModel;
32477         var colCount = cm.getColumnCount();
32478         for(var i = 0; i < colCount; i++){
32479             renderers[i] = cm.getRenderer(i);
32480         }
32481         return renderers;
32482     },
32483     
32484     getColumnIds : function(){
32485         var ids = [];
32486         var cm = this.grid.colModel;
32487         var colCount = cm.getColumnCount();
32488         for(var i = 0; i < colCount; i++){
32489             ids[i] = cm.getColumnId(i);
32490         }
32491         return ids;
32492     },
32493     
32494     getDataIndexes : function(){
32495         if(!this.indexMap){
32496             this.indexMap = this.buildIndexMap();
32497         }
32498         return this.indexMap.colToData;
32499     },
32500     
32501     getColumnIndexByDataIndex : function(dataIndex){
32502         if(!this.indexMap){
32503             this.indexMap = this.buildIndexMap();
32504         }
32505         return this.indexMap.dataToCol[dataIndex];
32506     },
32507     
32508     /**
32509      * Set a css style for a column dynamically. 
32510      * @param {Number} colIndex The index of the column
32511      * @param {String} name The css property name
32512      * @param {String} value The css value
32513      */
32514     setCSSStyle : function(colIndex, name, value){
32515         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32516         Roo.util.CSS.updateRule(selector, name, value);
32517     },
32518     
32519     generateRules : function(cm){
32520         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32521         Roo.util.CSS.removeStyleSheet(rulesId);
32522         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32523             var cid = cm.getColumnId(i);
32524             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32525                          this.tdSelector, cid, " {\n}\n",
32526                          this.hdSelector, cid, " {\n}\n",
32527                          this.splitSelector, cid, " {\n}\n");
32528         }
32529         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32530     }
32531 });/*
32532  * Based on:
32533  * Ext JS Library 1.1.1
32534  * Copyright(c) 2006-2007, Ext JS, LLC.
32535  *
32536  * Originally Released Under LGPL - original licence link has changed is not relivant.
32537  *
32538  * Fork - LGPL
32539  * <script type="text/javascript">
32540  */
32541
32542 // private
32543 // This is a support class used internally by the Grid components
32544 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32545     this.grid = grid;
32546     this.view = grid.getView();
32547     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32548     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32549     if(hd2){
32550         this.setHandleElId(Roo.id(hd));
32551         this.setOuterHandleElId(Roo.id(hd2));
32552     }
32553     this.scroll = false;
32554 };
32555 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32556     maxDragWidth: 120,
32557     getDragData : function(e){
32558         var t = Roo.lib.Event.getTarget(e);
32559         var h = this.view.findHeaderCell(t);
32560         if(h){
32561             return {ddel: h.firstChild, header:h};
32562         }
32563         return false;
32564     },
32565
32566     onInitDrag : function(e){
32567         this.view.headersDisabled = true;
32568         var clone = this.dragData.ddel.cloneNode(true);
32569         clone.id = Roo.id();
32570         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32571         this.proxy.update(clone);
32572         return true;
32573     },
32574
32575     afterValidDrop : function(){
32576         var v = this.view;
32577         setTimeout(function(){
32578             v.headersDisabled = false;
32579         }, 50);
32580     },
32581
32582     afterInvalidDrop : function(){
32583         var v = this.view;
32584         setTimeout(function(){
32585             v.headersDisabled = false;
32586         }, 50);
32587     }
32588 });
32589 /*
32590  * Based on:
32591  * Ext JS Library 1.1.1
32592  * Copyright(c) 2006-2007, Ext JS, LLC.
32593  *
32594  * Originally Released Under LGPL - original licence link has changed is not relivant.
32595  *
32596  * Fork - LGPL
32597  * <script type="text/javascript">
32598  */
32599 // private
32600 // This is a support class used internally by the Grid components
32601 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32602     this.grid = grid;
32603     this.view = grid.getView();
32604     // split the proxies so they don't interfere with mouse events
32605     this.proxyTop = Roo.DomHelper.append(document.body, {
32606         cls:"col-move-top", html:"&#160;"
32607     }, true);
32608     this.proxyBottom = Roo.DomHelper.append(document.body, {
32609         cls:"col-move-bottom", html:"&#160;"
32610     }, true);
32611     this.proxyTop.hide = this.proxyBottom.hide = function(){
32612         this.setLeftTop(-100,-100);
32613         this.setStyle("visibility", "hidden");
32614     };
32615     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32616     // temporarily disabled
32617     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32618     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32619 };
32620 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32621     proxyOffsets : [-4, -9],
32622     fly: Roo.Element.fly,
32623
32624     getTargetFromEvent : function(e){
32625         var t = Roo.lib.Event.getTarget(e);
32626         var cindex = this.view.findCellIndex(t);
32627         if(cindex !== false){
32628             return this.view.getHeaderCell(cindex);
32629         }
32630     },
32631
32632     nextVisible : function(h){
32633         var v = this.view, cm = this.grid.colModel;
32634         h = h.nextSibling;
32635         while(h){
32636             if(!cm.isHidden(v.getCellIndex(h))){
32637                 return h;
32638             }
32639             h = h.nextSibling;
32640         }
32641         return null;
32642     },
32643
32644     prevVisible : function(h){
32645         var v = this.view, cm = this.grid.colModel;
32646         h = h.prevSibling;
32647         while(h){
32648             if(!cm.isHidden(v.getCellIndex(h))){
32649                 return h;
32650             }
32651             h = h.prevSibling;
32652         }
32653         return null;
32654     },
32655
32656     positionIndicator : function(h, n, e){
32657         var x = Roo.lib.Event.getPageX(e);
32658         var r = Roo.lib.Dom.getRegion(n.firstChild);
32659         var px, pt, py = r.top + this.proxyOffsets[1];
32660         if((r.right - x) <= (r.right-r.left)/2){
32661             px = r.right+this.view.borderWidth;
32662             pt = "after";
32663         }else{
32664             px = r.left;
32665             pt = "before";
32666         }
32667         var oldIndex = this.view.getCellIndex(h);
32668         var newIndex = this.view.getCellIndex(n);
32669
32670         if(this.grid.colModel.isFixed(newIndex)){
32671             return false;
32672         }
32673
32674         var locked = this.grid.colModel.isLocked(newIndex);
32675
32676         if(pt == "after"){
32677             newIndex++;
32678         }
32679         if(oldIndex < newIndex){
32680             newIndex--;
32681         }
32682         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32683             return false;
32684         }
32685         px +=  this.proxyOffsets[0];
32686         this.proxyTop.setLeftTop(px, py);
32687         this.proxyTop.show();
32688         if(!this.bottomOffset){
32689             this.bottomOffset = this.view.mainHd.getHeight();
32690         }
32691         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32692         this.proxyBottom.show();
32693         return pt;
32694     },
32695
32696     onNodeEnter : function(n, dd, e, data){
32697         if(data.header != n){
32698             this.positionIndicator(data.header, n, e);
32699         }
32700     },
32701
32702     onNodeOver : function(n, dd, e, data){
32703         var result = false;
32704         if(data.header != n){
32705             result = this.positionIndicator(data.header, n, e);
32706         }
32707         if(!result){
32708             this.proxyTop.hide();
32709             this.proxyBottom.hide();
32710         }
32711         return result ? this.dropAllowed : this.dropNotAllowed;
32712     },
32713
32714     onNodeOut : function(n, dd, e, data){
32715         this.proxyTop.hide();
32716         this.proxyBottom.hide();
32717     },
32718
32719     onNodeDrop : function(n, dd, e, data){
32720         var h = data.header;
32721         if(h != n){
32722             var cm = this.grid.colModel;
32723             var x = Roo.lib.Event.getPageX(e);
32724             var r = Roo.lib.Dom.getRegion(n.firstChild);
32725             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32726             var oldIndex = this.view.getCellIndex(h);
32727             var newIndex = this.view.getCellIndex(n);
32728             var locked = cm.isLocked(newIndex);
32729             if(pt == "after"){
32730                 newIndex++;
32731             }
32732             if(oldIndex < newIndex){
32733                 newIndex--;
32734             }
32735             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32736                 return false;
32737             }
32738             cm.setLocked(oldIndex, locked, true);
32739             cm.moveColumn(oldIndex, newIndex);
32740             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32741             return true;
32742         }
32743         return false;
32744     }
32745 });
32746 /*
32747  * Based on:
32748  * Ext JS Library 1.1.1
32749  * Copyright(c) 2006-2007, Ext JS, LLC.
32750  *
32751  * Originally Released Under LGPL - original licence link has changed is not relivant.
32752  *
32753  * Fork - LGPL
32754  * <script type="text/javascript">
32755  */
32756   
32757 /**
32758  * @class Roo.grid.GridView
32759  * @extends Roo.util.Observable
32760  *
32761  * @constructor
32762  * @param {Object} config
32763  */
32764 Roo.grid.GridView = function(config){
32765     Roo.grid.GridView.superclass.constructor.call(this);
32766     this.el = null;
32767
32768     Roo.apply(this, config);
32769 };
32770
32771 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32772
32773     /**
32774      * Override this function to apply custom css classes to rows during rendering
32775      * @param {Record} record The record
32776      * @param {Number} index
32777      * @method getRowClass
32778      */
32779     rowClass : "x-grid-row",
32780
32781     cellClass : "x-grid-col",
32782
32783     tdClass : "x-grid-td",
32784
32785     hdClass : "x-grid-hd",
32786
32787     splitClass : "x-grid-split",
32788
32789     sortClasses : ["sort-asc", "sort-desc"],
32790
32791     enableMoveAnim : false,
32792
32793     hlColor: "C3DAF9",
32794
32795     dh : Roo.DomHelper,
32796
32797     fly : Roo.Element.fly,
32798
32799     css : Roo.util.CSS,
32800
32801     borderWidth: 1,
32802
32803     splitOffset: 3,
32804
32805     scrollIncrement : 22,
32806
32807     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32808
32809     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32810
32811     bind : function(ds, cm){
32812         if(this.ds){
32813             this.ds.un("load", this.onLoad, this);
32814             this.ds.un("datachanged", this.onDataChange, this);
32815             this.ds.un("add", this.onAdd, this);
32816             this.ds.un("remove", this.onRemove, this);
32817             this.ds.un("update", this.onUpdate, this);
32818             this.ds.un("clear", this.onClear, this);
32819         }
32820         if(ds){
32821             ds.on("load", this.onLoad, this);
32822             ds.on("datachanged", this.onDataChange, this);
32823             ds.on("add", this.onAdd, this);
32824             ds.on("remove", this.onRemove, this);
32825             ds.on("update", this.onUpdate, this);
32826             ds.on("clear", this.onClear, this);
32827         }
32828         this.ds = ds;
32829
32830         if(this.cm){
32831             this.cm.un("widthchange", this.onColWidthChange, this);
32832             this.cm.un("headerchange", this.onHeaderChange, this);
32833             this.cm.un("hiddenchange", this.onHiddenChange, this);
32834             this.cm.un("columnmoved", this.onColumnMove, this);
32835             this.cm.un("columnlockchange", this.onColumnLock, this);
32836         }
32837         if(cm){
32838             this.generateRules(cm);
32839             cm.on("widthchange", this.onColWidthChange, this);
32840             cm.on("headerchange", this.onHeaderChange, this);
32841             cm.on("hiddenchange", this.onHiddenChange, this);
32842             cm.on("columnmoved", this.onColumnMove, this);
32843             cm.on("columnlockchange", this.onColumnLock, this);
32844         }
32845         this.cm = cm;
32846     },
32847
32848     init: function(grid){
32849                 Roo.grid.GridView.superclass.init.call(this, grid);
32850
32851                 this.bind(grid.dataSource, grid.colModel);
32852
32853             grid.on("headerclick", this.handleHeaderClick, this);
32854
32855         if(grid.trackMouseOver){
32856             grid.on("mouseover", this.onRowOver, this);
32857                 grid.on("mouseout", this.onRowOut, this);
32858             }
32859             grid.cancelTextSelection = function(){};
32860                 this.gridId = grid.id;
32861
32862                 var tpls = this.templates || {};
32863
32864                 if(!tpls.master){
32865                     tpls.master = new Roo.Template(
32866                        '<div class="x-grid" hidefocus="true">',
32867                           '<div class="x-grid-topbar"></div>',
32868                           '<div class="x-grid-scroller"><div></div></div>',
32869                           '<div class="x-grid-locked">',
32870                               '<div class="x-grid-header">{lockedHeader}</div>',
32871                               '<div class="x-grid-body">{lockedBody}</div>',
32872                           "</div>",
32873                           '<div class="x-grid-viewport">',
32874                               '<div class="x-grid-header">{header}</div>',
32875                               '<div class="x-grid-body">{body}</div>',
32876                           "</div>",
32877                           '<div class="x-grid-bottombar"></div>',
32878                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32879                           '<div class="x-grid-resize-proxy">&#160;</div>',
32880                        "</div>"
32881                     );
32882                     tpls.master.disableformats = true;
32883                 }
32884
32885                 if(!tpls.header){
32886                     tpls.header = new Roo.Template(
32887                        '<table border="0" cellspacing="0" cellpadding="0">',
32888                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32889                        "</table>{splits}"
32890                     );
32891                     tpls.header.disableformats = true;
32892                 }
32893                 tpls.header.compile();
32894
32895                 if(!tpls.hcell){
32896                     tpls.hcell = new Roo.Template(
32897                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32898                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32899                         "</div></td>"
32900                      );
32901                      tpls.hcell.disableFormats = true;
32902                 }
32903                 tpls.hcell.compile();
32904
32905                 if(!tpls.hsplit){
32906                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
32907                     tpls.hsplit.disableFormats = true;
32908                 }
32909                 tpls.hsplit.compile();
32910
32911                 if(!tpls.body){
32912                     tpls.body = new Roo.Template(
32913                        '<table border="0" cellspacing="0" cellpadding="0">',
32914                        "<tbody>{rows}</tbody>",
32915                        "</table>"
32916                     );
32917                     tpls.body.disableFormats = true;
32918                 }
32919                 tpls.body.compile();
32920
32921                 if(!tpls.row){
32922                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32923                     tpls.row.disableFormats = true;
32924                 }
32925                 tpls.row.compile();
32926
32927                 if(!tpls.cell){
32928                     tpls.cell = new Roo.Template(
32929                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32930                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
32931                         "</td>"
32932                     );
32933             tpls.cell.disableFormats = true;
32934         }
32935                 tpls.cell.compile();
32936
32937                 this.templates = tpls;
32938         },
32939
32940         // remap these for backwards compat
32941     onColWidthChange : function(){
32942         this.updateColumns.apply(this, arguments);
32943     },
32944     onHeaderChange : function(){
32945         this.updateHeaders.apply(this, arguments);
32946     }, 
32947     onHiddenChange : function(){
32948         this.handleHiddenChange.apply(this, arguments);
32949     },
32950     onColumnMove : function(){
32951         this.handleColumnMove.apply(this, arguments);
32952     },
32953     onColumnLock : function(){
32954         this.handleLockChange.apply(this, arguments);
32955     },
32956
32957     onDataChange : function(){
32958         this.refresh();
32959         this.updateHeaderSortState();
32960     },
32961
32962         onClear : function(){
32963         this.refresh();
32964     },
32965
32966         onUpdate : function(ds, record){
32967         this.refreshRow(record);
32968     },
32969
32970     refreshRow : function(record){
32971         var ds = this.ds, index;
32972         if(typeof record == 'number'){
32973             index = record;
32974             record = ds.getAt(index);
32975         }else{
32976             index = ds.indexOf(record);
32977         }
32978         this.insertRows(ds, index, index, true);
32979         this.onRemove(ds, record, index+1, true);
32980         this.syncRowHeights(index, index);
32981         this.layout();
32982         this.fireEvent("rowupdated", this, index, record);
32983     },
32984
32985     onAdd : function(ds, records, index){
32986         this.insertRows(ds, index, index + (records.length-1));
32987     },
32988
32989     onRemove : function(ds, record, index, isUpdate){
32990         if(isUpdate !== true){
32991             this.fireEvent("beforerowremoved", this, index, record);
32992         }
32993         var bt = this.getBodyTable(), lt = this.getLockedTable();
32994         if(bt.rows[index]){
32995             bt.firstChild.removeChild(bt.rows[index]);
32996         }
32997         if(lt.rows[index]){
32998             lt.firstChild.removeChild(lt.rows[index]);
32999         }
33000         if(isUpdate !== true){
33001             this.stripeRows(index);
33002             this.syncRowHeights(index, index);
33003             this.layout();
33004             this.fireEvent("rowremoved", this, index, record);
33005         }
33006     },
33007
33008     onLoad : function(){
33009         this.scrollToTop();
33010     },
33011
33012     /**
33013      * Scrolls the grid to the top
33014      */
33015     scrollToTop : function(){
33016         if(this.scroller){
33017             this.scroller.dom.scrollTop = 0;
33018             this.syncScroll();
33019         }
33020     },
33021
33022     /**
33023      * Gets a panel in the header of the grid that can be used for toolbars etc.
33024      * After modifying the contents of this panel a call to grid.autoSize() may be
33025      * required to register any changes in size.
33026      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33027      * @return Roo.Element
33028      */
33029     getHeaderPanel : function(doShow){
33030         if(doShow){
33031             this.headerPanel.show();
33032         }
33033         return this.headerPanel;
33034         },
33035
33036         /**
33037      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33038      * After modifying the contents of this panel a call to grid.autoSize() may be
33039      * required to register any changes in size.
33040      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33041      * @return Roo.Element
33042      */
33043     getFooterPanel : function(doShow){
33044         if(doShow){
33045             this.footerPanel.show();
33046         }
33047         return this.footerPanel;
33048         },
33049
33050         initElements : function(){
33051             var E = Roo.Element;
33052             var el = this.grid.getGridEl().dom.firstChild;
33053             var cs = el.childNodes;
33054
33055             this.el = new E(el);
33056             this.headerPanel = new E(el.firstChild);
33057             this.headerPanel.enableDisplayMode("block");
33058
33059         this.scroller = new E(cs[1]);
33060             this.scrollSizer = new E(this.scroller.dom.firstChild);
33061
33062             this.lockedWrap = new E(cs[2]);
33063             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33064             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33065
33066             this.mainWrap = new E(cs[3]);
33067             this.mainHd = new E(this.mainWrap.dom.firstChild);
33068             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33069
33070             this.footerPanel = new E(cs[4]);
33071             this.footerPanel.enableDisplayMode("block");
33072
33073         this.focusEl = new E(cs[5]);
33074         this.focusEl.swallowEvent("click", true);
33075         this.resizeProxy = new E(cs[6]);
33076
33077             this.headerSelector = String.format(
33078                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33079                this.lockedHd.id, this.mainHd.id
33080             );
33081
33082             this.splitterSelector = String.format(
33083                '#{0} div.x-grid-split, #{1} div.x-grid-split',
33084                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33085             );
33086     },
33087     idToCssName : function(s)
33088     {
33089         return s.replace(/[^a-z0-9]+/ig, '-');
33090     },
33091
33092         getHeaderCell : function(index){
33093             return Roo.DomQuery.select(this.headerSelector)[index];
33094         },
33095
33096         getHeaderCellMeasure : function(index){
33097             return this.getHeaderCell(index).firstChild;
33098         },
33099
33100         getHeaderCellText : function(index){
33101             return this.getHeaderCell(index).firstChild.firstChild;
33102         },
33103
33104         getLockedTable : function(){
33105             return this.lockedBody.dom.firstChild;
33106         },
33107
33108         getBodyTable : function(){
33109             return this.mainBody.dom.firstChild;
33110         },
33111
33112         getLockedRow : function(index){
33113             return this.getLockedTable().rows[index];
33114         },
33115
33116         getRow : function(index){
33117             return this.getBodyTable().rows[index];
33118         },
33119
33120         getRowComposite : function(index){
33121             if(!this.rowEl){
33122                 this.rowEl = new Roo.CompositeElementLite();
33123             }
33124         var els = [], lrow, mrow;
33125         if(lrow = this.getLockedRow(index)){
33126             els.push(lrow);
33127         }
33128         if(mrow = this.getRow(index)){
33129             els.push(mrow);
33130         }
33131         this.rowEl.elements = els;
33132             return this.rowEl;
33133         },
33134
33135         getCell : function(rowIndex, colIndex){
33136             var locked = this.cm.getLockedCount();
33137             var source;
33138             if(colIndex < locked){
33139                 source = this.lockedBody.dom.firstChild;
33140             }else{
33141                 source = this.mainBody.dom.firstChild;
33142                 colIndex -= locked;
33143             }
33144         return source.rows[rowIndex].childNodes[colIndex];
33145         },
33146
33147         getCellText : function(rowIndex, colIndex){
33148             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33149         },
33150
33151         getCellBox : function(cell){
33152             var b = this.fly(cell).getBox();
33153         if(Roo.isOpera){ // opera fails to report the Y
33154             b.y = cell.offsetTop + this.mainBody.getY();
33155         }
33156         return b;
33157     },
33158
33159     getCellIndex : function(cell){
33160         var id = String(cell.className).match(this.cellRE);
33161         if(id){
33162             return parseInt(id[1], 10);
33163         }
33164         return 0;
33165     },
33166
33167     findHeaderIndex : function(n){
33168         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33169         return r ? this.getCellIndex(r) : false;
33170     },
33171
33172     findHeaderCell : function(n){
33173         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33174         return r ? r : false;
33175     },
33176
33177     findRowIndex : function(n){
33178         if(!n){
33179             return false;
33180         }
33181         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33182         return r ? r.rowIndex : false;
33183     },
33184
33185     findCellIndex : function(node){
33186         var stop = this.el.dom;
33187         while(node && node != stop){
33188             if(this.findRE.test(node.className)){
33189                 return this.getCellIndex(node);
33190             }
33191             node = node.parentNode;
33192         }
33193         return false;
33194     },
33195
33196     getColumnId : function(index){
33197             return this.cm.getColumnId(index);
33198         },
33199
33200         getSplitters : function(){
33201             if(this.splitterSelector){
33202                return Roo.DomQuery.select(this.splitterSelector);
33203             }else{
33204                 return null;
33205             }
33206         },
33207
33208         getSplitter : function(index){
33209             return this.getSplitters()[index];
33210         },
33211
33212     onRowOver : function(e, t){
33213         var row;
33214         if((row = this.findRowIndex(t)) !== false){
33215             this.getRowComposite(row).addClass("x-grid-row-over");
33216         }
33217     },
33218
33219     onRowOut : function(e, t){
33220         var row;
33221         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33222             this.getRowComposite(row).removeClass("x-grid-row-over");
33223         }
33224     },
33225
33226     renderHeaders : function(){
33227             var cm = this.cm;
33228         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33229         var cb = [], lb = [], sb = [], lsb = [], p = {};
33230         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33231             p.cellId = "x-grid-hd-0-" + i;
33232             p.splitId = "x-grid-csplit-0-" + i;
33233             p.id = cm.getColumnId(i);
33234             p.title = cm.getColumnTooltip(i) || "";
33235             p.value = cm.getColumnHeader(i) || "";
33236             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33237             if(!cm.isLocked(i)){
33238                 cb[cb.length] = ct.apply(p);
33239                 sb[sb.length] = st.apply(p);
33240             }else{
33241                 lb[lb.length] = ct.apply(p);
33242                 lsb[lsb.length] = st.apply(p);
33243             }
33244         }
33245         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33246                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33247         },
33248
33249         updateHeaders : function(){
33250         var html = this.renderHeaders();
33251         this.lockedHd.update(html[0]);
33252         this.mainHd.update(html[1]);
33253     },
33254
33255     /**
33256      * Focuses the specified row.
33257      * @param {Number} row The row index
33258      */
33259     focusRow : function(row){
33260         var x = this.scroller.dom.scrollLeft;
33261         this.focusCell(row, 0, false);
33262         this.scroller.dom.scrollLeft = x;
33263     },
33264
33265     /**
33266      * Focuses the specified cell.
33267      * @param {Number} row The row index
33268      * @param {Number} col The column index
33269      * @param {Boolean} hscroll false to disable horizontal scrolling
33270      */
33271     focusCell : function(row, col, hscroll){
33272         var el = this.ensureVisible(row, col, hscroll);
33273         this.focusEl.alignTo(el, "tl-tl");
33274         if(Roo.isGecko){
33275             this.focusEl.focus();
33276         }else{
33277             this.focusEl.focus.defer(1, this.focusEl);
33278         }
33279     },
33280
33281     /**
33282      * Scrolls the specified cell into view
33283      * @param {Number} row The row index
33284      * @param {Number} col The column index
33285      * @param {Boolean} hscroll false to disable horizontal scrolling
33286      */
33287     ensureVisible : function(row, col, hscroll){
33288         if(typeof row != "number"){
33289             row = row.rowIndex;
33290         }
33291         if(row < 0 && row >= this.ds.getCount()){
33292             return;
33293         }
33294         col = (col !== undefined ? col : 0);
33295         var cm = this.grid.colModel;
33296         while(cm.isHidden(col)){
33297             col++;
33298         }
33299
33300         var el = this.getCell(row, col);
33301         if(!el){
33302             return;
33303         }
33304         var c = this.scroller.dom;
33305
33306         var ctop = parseInt(el.offsetTop, 10);
33307         var cleft = parseInt(el.offsetLeft, 10);
33308         var cbot = ctop + el.offsetHeight;
33309         var cright = cleft + el.offsetWidth;
33310
33311         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33312         var stop = parseInt(c.scrollTop, 10);
33313         var sleft = parseInt(c.scrollLeft, 10);
33314         var sbot = stop + ch;
33315         var sright = sleft + c.clientWidth;
33316
33317         if(ctop < stop){
33318                 c.scrollTop = ctop;
33319         }else if(cbot > sbot){
33320             c.scrollTop = cbot-ch;
33321         }
33322
33323         if(hscroll !== false){
33324             if(cleft < sleft){
33325                 c.scrollLeft = cleft;
33326             }else if(cright > sright){
33327                 c.scrollLeft = cright-c.clientWidth;
33328             }
33329         }
33330         return el;
33331     },
33332
33333     updateColumns : function(){
33334         this.grid.stopEditing();
33335         var cm = this.grid.colModel, colIds = this.getColumnIds();
33336         //var totalWidth = cm.getTotalWidth();
33337         var pos = 0;
33338         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33339             //if(cm.isHidden(i)) continue;
33340             var w = cm.getColumnWidth(i);
33341             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33342             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33343         }
33344         this.updateSplitters();
33345     },
33346
33347     generateRules : function(cm){
33348         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33349         Roo.util.CSS.removeStyleSheet(rulesId);
33350         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33351             var cid = cm.getColumnId(i);
33352             var align = '';
33353             if(cm.config[i].align){
33354                 align = 'text-align:'+cm.config[i].align+';';
33355             }
33356             var hidden = '';
33357             if(cm.isHidden(i)){
33358                 hidden = 'display:none;';
33359             }
33360             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33361             ruleBuf.push(
33362                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33363                     this.hdSelector, cid, " {\n", align, width, "}\n",
33364                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33365                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33366         }
33367         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33368     },
33369
33370     updateSplitters : function(){
33371         var cm = this.cm, s = this.getSplitters();
33372         if(s){ // splitters not created yet
33373             var pos = 0, locked = true;
33374             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33375                 if(cm.isHidden(i)) continue;
33376                 var w = cm.getColumnWidth(i);
33377                 if(!cm.isLocked(i) && locked){
33378                     pos = 0;
33379                     locked = false;
33380                 }
33381                 pos += w;
33382                 s[i].style.left = (pos-this.splitOffset) + "px";
33383             }
33384         }
33385     },
33386
33387     handleHiddenChange : function(colModel, colIndex, hidden){
33388         if(hidden){
33389             this.hideColumn(colIndex);
33390         }else{
33391             this.unhideColumn(colIndex);
33392         }
33393     },
33394
33395     hideColumn : function(colIndex){
33396         var cid = this.getColumnId(colIndex);
33397         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33398         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33399         if(Roo.isSafari){
33400             this.updateHeaders();
33401         }
33402         this.updateSplitters();
33403         this.layout();
33404     },
33405
33406     unhideColumn : function(colIndex){
33407         var cid = this.getColumnId(colIndex);
33408         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33409         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33410
33411         if(Roo.isSafari){
33412             this.updateHeaders();
33413         }
33414         this.updateSplitters();
33415         this.layout();
33416     },
33417
33418     insertRows : function(dm, firstRow, lastRow, isUpdate){
33419         if(firstRow == 0 && lastRow == dm.getCount()-1){
33420             this.refresh();
33421         }else{
33422             if(!isUpdate){
33423                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33424             }
33425             var s = this.getScrollState();
33426             var markup = this.renderRows(firstRow, lastRow);
33427             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33428             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33429             this.restoreScroll(s);
33430             if(!isUpdate){
33431                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33432                 this.syncRowHeights(firstRow, lastRow);
33433                 this.stripeRows(firstRow);
33434                 this.layout();
33435             }
33436         }
33437     },
33438
33439     bufferRows : function(markup, target, index){
33440         var before = null, trows = target.rows, tbody = target.tBodies[0];
33441         if(index < trows.length){
33442             before = trows[index];
33443         }
33444         var b = document.createElement("div");
33445         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33446         var rows = b.firstChild.rows;
33447         for(var i = 0, len = rows.length; i < len; i++){
33448             if(before){
33449                 tbody.insertBefore(rows[0], before);
33450             }else{
33451                 tbody.appendChild(rows[0]);
33452             }
33453         }
33454         b.innerHTML = "";
33455         b = null;
33456     },
33457
33458     deleteRows : function(dm, firstRow, lastRow){
33459         if(dm.getRowCount()<1){
33460             this.fireEvent("beforerefresh", this);
33461             this.mainBody.update("");
33462             this.lockedBody.update("");
33463             this.fireEvent("refresh", this);
33464         }else{
33465             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33466             var bt = this.getBodyTable();
33467             var tbody = bt.firstChild;
33468             var rows = bt.rows;
33469             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33470                 tbody.removeChild(rows[firstRow]);
33471             }
33472             this.stripeRows(firstRow);
33473             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33474         }
33475     },
33476
33477     updateRows : function(dataSource, firstRow, lastRow){
33478         var s = this.getScrollState();
33479         this.refresh();
33480         this.restoreScroll(s);
33481     },
33482
33483     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33484         if(!noRefresh){
33485            this.refresh();
33486         }
33487         this.updateHeaderSortState();
33488     },
33489
33490     getScrollState : function(){
33491         var sb = this.scroller.dom;
33492         return {left: sb.scrollLeft, top: sb.scrollTop};
33493     },
33494
33495     stripeRows : function(startRow){
33496         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33497             return;
33498         }
33499         startRow = startRow || 0;
33500         var rows = this.getBodyTable().rows;
33501         var lrows = this.getLockedTable().rows;
33502         var cls = ' x-grid-row-alt ';
33503         for(var i = startRow, len = rows.length; i < len; i++){
33504             var row = rows[i], lrow = lrows[i];
33505             var isAlt = ((i+1) % 2 == 0);
33506             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33507             if(isAlt == hasAlt){
33508                 continue;
33509             }
33510             if(isAlt){
33511                 row.className += " x-grid-row-alt";
33512             }else{
33513                 row.className = row.className.replace("x-grid-row-alt", "");
33514             }
33515             if(lrow){
33516                 lrow.className = row.className;
33517             }
33518         }
33519     },
33520
33521     restoreScroll : function(state){
33522         var sb = this.scroller.dom;
33523         sb.scrollLeft = state.left;
33524         sb.scrollTop = state.top;
33525         this.syncScroll();
33526     },
33527
33528     syncScroll : function(){
33529         var sb = this.scroller.dom;
33530         var sh = this.mainHd.dom;
33531         var bs = this.mainBody.dom;
33532         var lv = this.lockedBody.dom;
33533         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33534         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33535     },
33536
33537     handleScroll : function(e){
33538         this.syncScroll();
33539         var sb = this.scroller.dom;
33540         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33541         e.stopEvent();
33542     },
33543
33544     handleWheel : function(e){
33545         var d = e.getWheelDelta();
33546         this.scroller.dom.scrollTop -= d*22;
33547         // set this here to prevent jumpy scrolling on large tables
33548         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33549         e.stopEvent();
33550     },
33551
33552     renderRows : function(startRow, endRow){
33553         // pull in all the crap needed to render rows
33554         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33555         var colCount = cm.getColumnCount();
33556
33557         if(ds.getCount() < 1){
33558             return ["", ""];
33559         }
33560
33561         // build a map for all the columns
33562         var cs = [];
33563         for(var i = 0; i < colCount; i++){
33564             var name = cm.getDataIndex(i);
33565             cs[i] = {
33566                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33567                 renderer : cm.getRenderer(i),
33568                 id : cm.getColumnId(i),
33569                 locked : cm.isLocked(i)
33570             };
33571         }
33572
33573         startRow = startRow || 0;
33574         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33575
33576         // records to render
33577         var rs = ds.getRange(startRow, endRow);
33578
33579         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33580     },
33581
33582     // As much as I hate to duplicate code, this was branched because FireFox really hates
33583     // [].join("") on strings. The performance difference was substantial enough to
33584     // branch this function
33585     doRender : Roo.isGecko ?
33586             function(cs, rs, ds, startRow, colCount, stripe){
33587                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33588                 // buffers
33589                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33590                 
33591                 var hasListener = this.grid.hasListener('rowclass');
33592                 var rowcfg = {};
33593                 for(var j = 0, len = rs.length; j < len; j++){
33594                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33595                     for(var i = 0; i < colCount; i++){
33596                         c = cs[i];
33597                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33598                         p.id = c.id;
33599                         p.css = p.attr = "";
33600                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33601                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33602                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33603                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33604                         }
33605                         var markup = ct.apply(p);
33606                         if(!c.locked){
33607                             cb+= markup;
33608                         }else{
33609                             lcb+= markup;
33610                         }
33611                     }
33612                     var alt = [];
33613                     if(stripe && ((rowIndex+1) % 2 == 0)){
33614                         alt.push("x-grid-row-alt")
33615                     }
33616                     if(r.dirty){
33617                         alt.push(  " x-grid-dirty-row");
33618                     }
33619                     rp.cells = lcb;
33620                     if(this.getRowClass){
33621                         alt.push(this.getRowClass(r, rowIndex));
33622                     }
33623                     if (hasListener) {
33624                         rowcfg = {
33625                              
33626                             record: r,
33627                             rowIndex : rowIndex,
33628                             rowClass : ''
33629                         }
33630                         this.grid.fireEvent('rowclass', this, rowcfg);
33631                         alt.push(rowcfg.rowClass);
33632                     }
33633                     rp.alt = alt.join(" ");
33634                     lbuf+= rt.apply(rp);
33635                     rp.cells = cb;
33636                     buf+=  rt.apply(rp);
33637                 }
33638                 return [lbuf, buf];
33639             } :
33640             function(cs, rs, ds, startRow, colCount, stripe){
33641                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33642                 // buffers
33643                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33644                 var hasListener = this.grid.hasListener('rowclass');
33645                 var rowcfg = {};
33646                 for(var j = 0, len = rs.length; j < len; j++){
33647                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33648                     for(var i = 0; i < colCount; i++){
33649                         c = cs[i];
33650                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33651                         p.id = c.id;
33652                         p.css = p.attr = "";
33653                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33654                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33655                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33656                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33657                         }
33658                         var markup = ct.apply(p);
33659                         if(!c.locked){
33660                             cb[cb.length] = markup;
33661                         }else{
33662                             lcb[lcb.length] = markup;
33663                         }
33664                     }
33665                     var alt = [];
33666                     if(stripe && ((rowIndex+1) % 2 == 0)){
33667                         alt.push( "x-grid-row-alt");
33668                     }
33669                     if(r.dirty){
33670                         alt.push(" x-grid-dirty-row");
33671                     }
33672                     rp.cells = lcb;
33673                     if(this.getRowClass){
33674                         alt.push( this.getRowClass(r, rowIndex));
33675                     }
33676                     if (hasListener) {
33677                         rowcfg = {
33678                              
33679                             record: r,
33680                             rowIndex : rowIndex,
33681                             rowClass : ''
33682                         }
33683                         this.grid.fireEvent('rowclass', this, rowcfg);
33684                         alt.push(rowcfg.rowClass);
33685                     }
33686                     rp.alt = alt.join(" ");
33687                     rp.cells = lcb.join("");
33688                     lbuf[lbuf.length] = rt.apply(rp);
33689                     rp.cells = cb.join("");
33690                     buf[buf.length] =  rt.apply(rp);
33691                 }
33692                 return [lbuf.join(""), buf.join("")];
33693             },
33694
33695     renderBody : function(){
33696         var markup = this.renderRows();
33697         var bt = this.templates.body;
33698         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33699     },
33700
33701     /**
33702      * Refreshes the grid
33703      * @param {Boolean} headersToo
33704      */
33705     refresh : function(headersToo){
33706         this.fireEvent("beforerefresh", this);
33707         this.grid.stopEditing();
33708         var result = this.renderBody();
33709         this.lockedBody.update(result[0]);
33710         this.mainBody.update(result[1]);
33711         if(headersToo === true){
33712             this.updateHeaders();
33713             this.updateColumns();
33714             this.updateSplitters();
33715             this.updateHeaderSortState();
33716         }
33717         this.syncRowHeights();
33718         this.layout();
33719         this.fireEvent("refresh", this);
33720     },
33721
33722     handleColumnMove : function(cm, oldIndex, newIndex){
33723         this.indexMap = null;
33724         var s = this.getScrollState();
33725         this.refresh(true);
33726         this.restoreScroll(s);
33727         this.afterMove(newIndex);
33728     },
33729
33730     afterMove : function(colIndex){
33731         if(this.enableMoveAnim && Roo.enableFx){
33732             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33733         }
33734     },
33735
33736     updateCell : function(dm, rowIndex, dataIndex){
33737         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33738         if(typeof colIndex == "undefined"){ // not present in grid
33739             return;
33740         }
33741         var cm = this.grid.colModel;
33742         var cell = this.getCell(rowIndex, colIndex);
33743         var cellText = this.getCellText(rowIndex, colIndex);
33744
33745         var p = {
33746             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33747             id : cm.getColumnId(colIndex),
33748             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33749         };
33750         var renderer = cm.getRenderer(colIndex);
33751         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33752         if(typeof val == "undefined" || val === "") val = "&#160;";
33753         cellText.innerHTML = val;
33754         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33755         this.syncRowHeights(rowIndex, rowIndex);
33756     },
33757
33758     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33759         var maxWidth = 0;
33760         if(this.grid.autoSizeHeaders){
33761             var h = this.getHeaderCellMeasure(colIndex);
33762             maxWidth = Math.max(maxWidth, h.scrollWidth);
33763         }
33764         var tb, index;
33765         if(this.cm.isLocked(colIndex)){
33766             tb = this.getLockedTable();
33767             index = colIndex;
33768         }else{
33769             tb = this.getBodyTable();
33770             index = colIndex - this.cm.getLockedCount();
33771         }
33772         if(tb && tb.rows){
33773             var rows = tb.rows;
33774             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33775             for(var i = 0; i < stopIndex; i++){
33776                 var cell = rows[i].childNodes[index].firstChild;
33777                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33778             }
33779         }
33780         return maxWidth + /*margin for error in IE*/ 5;
33781     },
33782     /**
33783      * Autofit a column to its content.
33784      * @param {Number} colIndex
33785      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33786      */
33787      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33788          if(this.cm.isHidden(colIndex)){
33789              return; // can't calc a hidden column
33790          }
33791         if(forceMinSize){
33792             var cid = this.cm.getColumnId(colIndex);
33793             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33794            if(this.grid.autoSizeHeaders){
33795                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33796            }
33797         }
33798         var newWidth = this.calcColumnWidth(colIndex);
33799         this.cm.setColumnWidth(colIndex,
33800             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33801         if(!suppressEvent){
33802             this.grid.fireEvent("columnresize", colIndex, newWidth);
33803         }
33804     },
33805
33806     /**
33807      * Autofits all columns to their content and then expands to fit any extra space in the grid
33808      */
33809      autoSizeColumns : function(){
33810         var cm = this.grid.colModel;
33811         var colCount = cm.getColumnCount();
33812         for(var i = 0; i < colCount; i++){
33813             this.autoSizeColumn(i, true, true);
33814         }
33815         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33816             this.fitColumns();
33817         }else{
33818             this.updateColumns();
33819             this.layout();
33820         }
33821     },
33822
33823     /**
33824      * Autofits all columns to the grid's width proportionate with their current size
33825      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33826      */
33827     fitColumns : function(reserveScrollSpace){
33828         var cm = this.grid.colModel;
33829         var colCount = cm.getColumnCount();
33830         var cols = [];
33831         var width = 0;
33832         var i, w;
33833         for (i = 0; i < colCount; i++){
33834             if(!cm.isHidden(i) && !cm.isFixed(i)){
33835                 w = cm.getColumnWidth(i);
33836                 cols.push(i);
33837                 cols.push(w);
33838                 width += w;
33839             }
33840         }
33841         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33842         if(reserveScrollSpace){
33843             avail -= 17;
33844         }
33845         var frac = (avail - cm.getTotalWidth())/width;
33846         while (cols.length){
33847             w = cols.pop();
33848             i = cols.pop();
33849             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33850         }
33851         this.updateColumns();
33852         this.layout();
33853     },
33854
33855     onRowSelect : function(rowIndex){
33856         var row = this.getRowComposite(rowIndex);
33857         row.addClass("x-grid-row-selected");
33858     },
33859
33860     onRowDeselect : function(rowIndex){
33861         var row = this.getRowComposite(rowIndex);
33862         row.removeClass("x-grid-row-selected");
33863     },
33864
33865     onCellSelect : function(row, col){
33866         var cell = this.getCell(row, col);
33867         if(cell){
33868             Roo.fly(cell).addClass("x-grid-cell-selected");
33869         }
33870     },
33871
33872     onCellDeselect : function(row, col){
33873         var cell = this.getCell(row, col);
33874         if(cell){
33875             Roo.fly(cell).removeClass("x-grid-cell-selected");
33876         }
33877     },
33878
33879     updateHeaderSortState : function(){
33880         var state = this.ds.getSortState();
33881         if(!state){
33882             return;
33883         }
33884         this.sortState = state;
33885         var sortColumn = this.cm.findColumnIndex(state.field);
33886         if(sortColumn != -1){
33887             var sortDir = state.direction;
33888             var sc = this.sortClasses;
33889             var hds = this.el.select(this.headerSelector).removeClass(sc);
33890             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33891         }
33892     },
33893
33894     handleHeaderClick : function(g, index){
33895         if(this.headersDisabled){
33896             return;
33897         }
33898         var dm = g.dataSource, cm = g.colModel;
33899             if(!cm.isSortable(index)){
33900             return;
33901         }
33902             g.stopEditing();
33903         dm.sort(cm.getDataIndex(index));
33904     },
33905
33906
33907     destroy : function(){
33908         if(this.colMenu){
33909             this.colMenu.removeAll();
33910             Roo.menu.MenuMgr.unregister(this.colMenu);
33911             this.colMenu.getEl().remove();
33912             delete this.colMenu;
33913         }
33914         if(this.hmenu){
33915             this.hmenu.removeAll();
33916             Roo.menu.MenuMgr.unregister(this.hmenu);
33917             this.hmenu.getEl().remove();
33918             delete this.hmenu;
33919         }
33920         if(this.grid.enableColumnMove){
33921             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33922             if(dds){
33923                 for(var dd in dds){
33924                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
33925                         var elid = dds[dd].dragElId;
33926                         dds[dd].unreg();
33927                         Roo.get(elid).remove();
33928                     } else if(dds[dd].config.isTarget){
33929                         dds[dd].proxyTop.remove();
33930                         dds[dd].proxyBottom.remove();
33931                         dds[dd].unreg();
33932                     }
33933                     if(Roo.dd.DDM.locationCache[dd]){
33934                         delete Roo.dd.DDM.locationCache[dd];
33935                     }
33936                 }
33937                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33938             }
33939         }
33940         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
33941         this.bind(null, null);
33942         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
33943     },
33944
33945     handleLockChange : function(){
33946         this.refresh(true);
33947     },
33948
33949     onDenyColumnLock : function(){
33950
33951     },
33952
33953     onDenyColumnHide : function(){
33954
33955     },
33956
33957     handleHdMenuClick : function(item){
33958         var index = this.hdCtxIndex;
33959         var cm = this.cm, ds = this.ds;
33960         switch(item.id){
33961             case "asc":
33962                 ds.sort(cm.getDataIndex(index), "ASC");
33963                 break;
33964             case "desc":
33965                 ds.sort(cm.getDataIndex(index), "DESC");
33966                 break;
33967             case "lock":
33968                 var lc = cm.getLockedCount();
33969                 if(cm.getColumnCount(true) <= lc+1){
33970                     this.onDenyColumnLock();
33971                     return;
33972                 }
33973                 if(lc != index){
33974                     cm.setLocked(index, true, true);
33975                     cm.moveColumn(index, lc);
33976                     this.grid.fireEvent("columnmove", index, lc);
33977                 }else{
33978                     cm.setLocked(index, true);
33979                 }
33980             break;
33981             case "unlock":
33982                 var lc = cm.getLockedCount();
33983                 if((lc-1) != index){
33984                     cm.setLocked(index, false, true);
33985                     cm.moveColumn(index, lc-1);
33986                     this.grid.fireEvent("columnmove", index, lc-1);
33987                 }else{
33988                     cm.setLocked(index, false);
33989                 }
33990             break;
33991             default:
33992                 index = cm.getIndexById(item.id.substr(4));
33993                 if(index != -1){
33994                     if(item.checked && cm.getColumnCount(true) <= 1){
33995                         this.onDenyColumnHide();
33996                         return false;
33997                     }
33998                     cm.setHidden(index, item.checked);
33999                 }
34000         }
34001         return true;
34002     },
34003
34004     beforeColMenuShow : function(){
34005         var cm = this.cm,  colCount = cm.getColumnCount();
34006         this.colMenu.removeAll();
34007         for(var i = 0; i < colCount; i++){
34008             this.colMenu.add(new Roo.menu.CheckItem({
34009                 id: "col-"+cm.getColumnId(i),
34010                 text: cm.getColumnHeader(i),
34011                 checked: !cm.isHidden(i),
34012                 hideOnClick:false
34013             }));
34014         }
34015     },
34016
34017     handleHdCtx : function(g, index, e){
34018         e.stopEvent();
34019         var hd = this.getHeaderCell(index);
34020         this.hdCtxIndex = index;
34021         var ms = this.hmenu.items, cm = this.cm;
34022         ms.get("asc").setDisabled(!cm.isSortable(index));
34023         ms.get("desc").setDisabled(!cm.isSortable(index));
34024         if(this.grid.enableColLock !== false){
34025             ms.get("lock").setDisabled(cm.isLocked(index));
34026             ms.get("unlock").setDisabled(!cm.isLocked(index));
34027         }
34028         this.hmenu.show(hd, "tl-bl");
34029     },
34030
34031     handleHdOver : function(e){
34032         var hd = this.findHeaderCell(e.getTarget());
34033         if(hd && !this.headersDisabled){
34034             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34035                this.fly(hd).addClass("x-grid-hd-over");
34036             }
34037         }
34038     },
34039
34040     handleHdOut : function(e){
34041         var hd = this.findHeaderCell(e.getTarget());
34042         if(hd){
34043             this.fly(hd).removeClass("x-grid-hd-over");
34044         }
34045     },
34046
34047     handleSplitDblClick : function(e, t){
34048         var i = this.getCellIndex(t);
34049         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34050             this.autoSizeColumn(i, true);
34051             this.layout();
34052         }
34053     },
34054
34055     render : function(){
34056
34057         var cm = this.cm;
34058         var colCount = cm.getColumnCount();
34059
34060         if(this.grid.monitorWindowResize === true){
34061             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34062         }
34063         var header = this.renderHeaders();
34064         var body = this.templates.body.apply({rows:""});
34065         var html = this.templates.master.apply({
34066             lockedBody: body,
34067             body: body,
34068             lockedHeader: header[0],
34069             header: header[1]
34070         });
34071
34072         //this.updateColumns();
34073
34074         this.grid.getGridEl().dom.innerHTML = html;
34075
34076         this.initElements();
34077         
34078         // a kludge to fix the random scolling effect in webkit
34079         this.el.on("scroll", function() {
34080             this.el.dom.scrollTop=0; // hopefully not recursive..
34081         },this);
34082
34083         this.scroller.on("scroll", this.handleScroll, this);
34084         this.lockedBody.on("mousewheel", this.handleWheel, this);
34085         this.mainBody.on("mousewheel", this.handleWheel, this);
34086
34087         this.mainHd.on("mouseover", this.handleHdOver, this);
34088         this.mainHd.on("mouseout", this.handleHdOut, this);
34089         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34090                 {delegate: "."+this.splitClass});
34091
34092         this.lockedHd.on("mouseover", this.handleHdOver, this);
34093         this.lockedHd.on("mouseout", this.handleHdOut, this);
34094         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34095                 {delegate: "."+this.splitClass});
34096
34097         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34098             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34099         }
34100
34101         this.updateSplitters();
34102
34103         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34104             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34105             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34106         }
34107
34108         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34109             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34110             this.hmenu.add(
34111                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34112                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34113             );
34114             if(this.grid.enableColLock !== false){
34115                 this.hmenu.add('-',
34116                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34117                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34118                 );
34119             }
34120             if(this.grid.enableColumnHide !== false){
34121
34122                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34123                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34124                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34125
34126                 this.hmenu.add('-',
34127                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34128                 );
34129             }
34130             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34131
34132             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34133         }
34134
34135         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34136             this.dd = new Roo.grid.GridDragZone(this.grid, {
34137                 ddGroup : this.grid.ddGroup || 'GridDD'
34138             });
34139         }
34140
34141         /*
34142         for(var i = 0; i < colCount; i++){
34143             if(cm.isHidden(i)){
34144                 this.hideColumn(i);
34145             }
34146             if(cm.config[i].align){
34147                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34148                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34149             }
34150         }*/
34151         
34152         this.updateHeaderSortState();
34153
34154         this.beforeInitialResize();
34155         this.layout(true);
34156
34157         // two part rendering gives faster view to the user
34158         this.renderPhase2.defer(1, this);
34159     },
34160
34161     renderPhase2 : function(){
34162         // render the rows now
34163         this.refresh();
34164         if(this.grid.autoSizeColumns){
34165             this.autoSizeColumns();
34166         }
34167     },
34168
34169     beforeInitialResize : function(){
34170
34171     },
34172
34173     onColumnSplitterMoved : function(i, w){
34174         this.userResized = true;
34175         var cm = this.grid.colModel;
34176         cm.setColumnWidth(i, w, true);
34177         var cid = cm.getColumnId(i);
34178         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34179         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34180         this.updateSplitters();
34181         this.layout();
34182         this.grid.fireEvent("columnresize", i, w);
34183     },
34184
34185     syncRowHeights : function(startIndex, endIndex){
34186         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34187             startIndex = startIndex || 0;
34188             var mrows = this.getBodyTable().rows;
34189             var lrows = this.getLockedTable().rows;
34190             var len = mrows.length-1;
34191             endIndex = Math.min(endIndex || len, len);
34192             for(var i = startIndex; i <= endIndex; i++){
34193                 var m = mrows[i], l = lrows[i];
34194                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34195                 m.style.height = l.style.height = h + "px";
34196             }
34197         }
34198     },
34199
34200     layout : function(initialRender, is2ndPass){
34201         var g = this.grid;
34202         var auto = g.autoHeight;
34203         var scrollOffset = 16;
34204         var c = g.getGridEl(), cm = this.cm,
34205                 expandCol = g.autoExpandColumn,
34206                 gv = this;
34207         //c.beginMeasure();
34208
34209         if(!c.dom.offsetWidth){ // display:none?
34210             if(initialRender){
34211                 this.lockedWrap.show();
34212                 this.mainWrap.show();
34213             }
34214             return;
34215         }
34216
34217         var hasLock = this.cm.isLocked(0);
34218
34219         var tbh = this.headerPanel.getHeight();
34220         var bbh = this.footerPanel.getHeight();
34221
34222         if(auto){
34223             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34224             var newHeight = ch + c.getBorderWidth("tb");
34225             if(g.maxHeight){
34226                 newHeight = Math.min(g.maxHeight, newHeight);
34227             }
34228             c.setHeight(newHeight);
34229         }
34230
34231         if(g.autoWidth){
34232             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34233         }
34234
34235         var s = this.scroller;
34236
34237         var csize = c.getSize(true);
34238
34239         this.el.setSize(csize.width, csize.height);
34240
34241         this.headerPanel.setWidth(csize.width);
34242         this.footerPanel.setWidth(csize.width);
34243
34244         var hdHeight = this.mainHd.getHeight();
34245         var vw = csize.width;
34246         var vh = csize.height - (tbh + bbh);
34247
34248         s.setSize(vw, vh);
34249
34250         var bt = this.getBodyTable();
34251         var ltWidth = hasLock ?
34252                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34253
34254         var scrollHeight = bt.offsetHeight;
34255         var scrollWidth = ltWidth + bt.offsetWidth;
34256         var vscroll = false, hscroll = false;
34257
34258         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34259
34260         var lw = this.lockedWrap, mw = this.mainWrap;
34261         var lb = this.lockedBody, mb = this.mainBody;
34262
34263         setTimeout(function(){
34264             var t = s.dom.offsetTop;
34265             var w = s.dom.clientWidth,
34266                 h = s.dom.clientHeight;
34267
34268             lw.setTop(t);
34269             lw.setSize(ltWidth, h);
34270
34271             mw.setLeftTop(ltWidth, t);
34272             mw.setSize(w-ltWidth, h);
34273
34274             lb.setHeight(h-hdHeight);
34275             mb.setHeight(h-hdHeight);
34276
34277             if(is2ndPass !== true && !gv.userResized && expandCol){
34278                 // high speed resize without full column calculation
34279                 
34280                 var ci = cm.getIndexById(expandCol);
34281                 if (ci < 0) {
34282                     ci = cm.findColumnIndex(expandCol);
34283                 }
34284                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34285                 var expandId = cm.getColumnId(ci);
34286                 var  tw = cm.getTotalWidth(false);
34287                 var currentWidth = cm.getColumnWidth(ci);
34288                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34289                 if(currentWidth != cw){
34290                     cm.setColumnWidth(ci, cw, true);
34291                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34292                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34293                     gv.updateSplitters();
34294                     gv.layout(false, true);
34295                 }
34296             }
34297
34298             if(initialRender){
34299                 lw.show();
34300                 mw.show();
34301             }
34302             //c.endMeasure();
34303         }, 10);
34304     },
34305
34306     onWindowResize : function(){
34307         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34308             return;
34309         }
34310         this.layout();
34311     },
34312
34313     appendFooter : function(parentEl){
34314         return null;
34315     },
34316
34317     sortAscText : "Sort Ascending",
34318     sortDescText : "Sort Descending",
34319     lockText : "Lock Column",
34320     unlockText : "Unlock Column",
34321     columnsText : "Columns"
34322 });
34323
34324
34325 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34326     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34327     this.proxy.el.addClass('x-grid3-col-dd');
34328 };
34329
34330 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34331     handleMouseDown : function(e){
34332
34333     },
34334
34335     callHandleMouseDown : function(e){
34336         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34337     }
34338 });
34339 /*
34340  * Based on:
34341  * Ext JS Library 1.1.1
34342  * Copyright(c) 2006-2007, Ext JS, LLC.
34343  *
34344  * Originally Released Under LGPL - original licence link has changed is not relivant.
34345  *
34346  * Fork - LGPL
34347  * <script type="text/javascript">
34348  */
34349  
34350 // private
34351 // This is a support class used internally by the Grid components
34352 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34353     this.grid = grid;
34354     this.view = grid.getView();
34355     this.proxy = this.view.resizeProxy;
34356     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34357         "gridSplitters" + this.grid.getGridEl().id, {
34358         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34359     });
34360     this.setHandleElId(Roo.id(hd));
34361     this.setOuterHandleElId(Roo.id(hd2));
34362     this.scroll = false;
34363 };
34364 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34365     fly: Roo.Element.fly,
34366
34367     b4StartDrag : function(x, y){
34368         this.view.headersDisabled = true;
34369         this.proxy.setHeight(this.view.mainWrap.getHeight());
34370         var w = this.cm.getColumnWidth(this.cellIndex);
34371         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34372         this.resetConstraints();
34373         this.setXConstraint(minw, 1000);
34374         this.setYConstraint(0, 0);
34375         this.minX = x - minw;
34376         this.maxX = x + 1000;
34377         this.startPos = x;
34378         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34379     },
34380
34381
34382     handleMouseDown : function(e){
34383         ev = Roo.EventObject.setEvent(e);
34384         var t = this.fly(ev.getTarget());
34385         if(t.hasClass("x-grid-split")){
34386             this.cellIndex = this.view.getCellIndex(t.dom);
34387             this.split = t.dom;
34388             this.cm = this.grid.colModel;
34389             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34390                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34391             }
34392         }
34393     },
34394
34395     endDrag : function(e){
34396         this.view.headersDisabled = false;
34397         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34398         var diff = endX - this.startPos;
34399         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34400     },
34401
34402     autoOffset : function(){
34403         this.setDelta(0,0);
34404     }
34405 });/*
34406  * Based on:
34407  * Ext JS Library 1.1.1
34408  * Copyright(c) 2006-2007, Ext JS, LLC.
34409  *
34410  * Originally Released Under LGPL - original licence link has changed is not relivant.
34411  *
34412  * Fork - LGPL
34413  * <script type="text/javascript">
34414  */
34415  
34416 // private
34417 // This is a support class used internally by the Grid components
34418 Roo.grid.GridDragZone = function(grid, config){
34419     this.view = grid.getView();
34420     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34421     if(this.view.lockedBody){
34422         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34423         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34424     }
34425     this.scroll = false;
34426     this.grid = grid;
34427     this.ddel = document.createElement('div');
34428     this.ddel.className = 'x-grid-dd-wrap';
34429 };
34430
34431 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34432     ddGroup : "GridDD",
34433
34434     getDragData : function(e){
34435         var t = Roo.lib.Event.getTarget(e);
34436         var rowIndex = this.view.findRowIndex(t);
34437         if(rowIndex !== false){
34438             var sm = this.grid.selModel;
34439             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34440               //  sm.mouseDown(e, t);
34441             //}
34442             if (e.hasModifier()){
34443                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34444             }
34445             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
34446         }
34447         return false;
34448     },
34449
34450     onInitDrag : function(e){
34451         var data = this.dragData;
34452         this.ddel.innerHTML = this.grid.getDragDropText();
34453         this.proxy.update(this.ddel);
34454         // fire start drag?
34455     },
34456
34457     afterRepair : function(){
34458         this.dragging = false;
34459     },
34460
34461     getRepairXY : function(e, data){
34462         return false;
34463     },
34464
34465     onEndDrag : function(data, e){
34466         // fire end drag?
34467     },
34468
34469     onValidDrop : function(dd, e, id){
34470         // fire drag drop?
34471         this.hideProxy();
34472     },
34473
34474     beforeInvalidDrop : function(e, id){
34475
34476     }
34477 });/*
34478  * Based on:
34479  * Ext JS Library 1.1.1
34480  * Copyright(c) 2006-2007, Ext JS, LLC.
34481  *
34482  * Originally Released Under LGPL - original licence link has changed is not relivant.
34483  *
34484  * Fork - LGPL
34485  * <script type="text/javascript">
34486  */
34487  
34488
34489 /**
34490  * @class Roo.grid.ColumnModel
34491  * @extends Roo.util.Observable
34492  * This is the default implementation of a ColumnModel used by the Grid. It defines
34493  * the columns in the grid.
34494  * <br>Usage:<br>
34495  <pre><code>
34496  var colModel = new Roo.grid.ColumnModel([
34497         {header: "Ticker", width: 60, sortable: true, locked: true},
34498         {header: "Company Name", width: 150, sortable: true},
34499         {header: "Market Cap.", width: 100, sortable: true},
34500         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34501         {header: "Employees", width: 100, sortable: true, resizable: false}
34502  ]);
34503  </code></pre>
34504  * <p>
34505  
34506  * The config options listed for this class are options which may appear in each
34507  * individual column definition.
34508  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34509  * @constructor
34510  * @param {Object} config An Array of column config objects. See this class's
34511  * config objects for details.
34512 */
34513 Roo.grid.ColumnModel = function(config){
34514         /**
34515      * The config passed into the constructor
34516      */
34517     this.config = config;
34518     this.lookup = {};
34519
34520     // if no id, create one
34521     // if the column does not have a dataIndex mapping,
34522     // map it to the order it is in the config
34523     for(var i = 0, len = config.length; i < len; i++){
34524         var c = config[i];
34525         if(typeof c.dataIndex == "undefined"){
34526             c.dataIndex = i;
34527         }
34528         if(typeof c.renderer == "string"){
34529             c.renderer = Roo.util.Format[c.renderer];
34530         }
34531         if(typeof c.id == "undefined"){
34532             c.id = Roo.id();
34533         }
34534         if(c.editor && c.editor.xtype){
34535             c.editor  = Roo.factory(c.editor, Roo.grid);
34536         }
34537         if(c.editor && c.editor.isFormField){
34538             c.editor = new Roo.grid.GridEditor(c.editor);
34539         }
34540         this.lookup[c.id] = c;
34541     }
34542
34543     /**
34544      * The width of columns which have no width specified (defaults to 100)
34545      * @type Number
34546      */
34547     this.defaultWidth = 100;
34548
34549     /**
34550      * Default sortable of columns which have no sortable specified (defaults to false)
34551      * @type Boolean
34552      */
34553     this.defaultSortable = false;
34554
34555     this.addEvents({
34556         /**
34557              * @event widthchange
34558              * Fires when the width of a column changes.
34559              * @param {ColumnModel} this
34560              * @param {Number} columnIndex The column index
34561              * @param {Number} newWidth The new width
34562              */
34563             "widthchange": true,
34564         /**
34565              * @event headerchange
34566              * Fires when the text of a header changes.
34567              * @param {ColumnModel} this
34568              * @param {Number} columnIndex The column index
34569              * @param {Number} newText The new header text
34570              */
34571             "headerchange": true,
34572         /**
34573              * @event hiddenchange
34574              * Fires when a column is hidden or "unhidden".
34575              * @param {ColumnModel} this
34576              * @param {Number} columnIndex The column index
34577              * @param {Boolean} hidden true if hidden, false otherwise
34578              */
34579             "hiddenchange": true,
34580             /**
34581          * @event columnmoved
34582          * Fires when a column is moved.
34583          * @param {ColumnModel} this
34584          * @param {Number} oldIndex
34585          * @param {Number} newIndex
34586          */
34587         "columnmoved" : true,
34588         /**
34589          * @event columlockchange
34590          * Fires when a column's locked state is changed
34591          * @param {ColumnModel} this
34592          * @param {Number} colIndex
34593          * @param {Boolean} locked true if locked
34594          */
34595         "columnlockchange" : true
34596     });
34597     Roo.grid.ColumnModel.superclass.constructor.call(this);
34598 };
34599 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34600     /**
34601      * @cfg {String} header The header text to display in the Grid view.
34602      */
34603     /**
34604      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34605      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34606      * specified, the column's index is used as an index into the Record's data Array.
34607      */
34608     /**
34609      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34610      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34611      */
34612     /**
34613      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34614      * Defaults to the value of the {@link #defaultSortable} property.
34615      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34616      */
34617     /**
34618      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34619      */
34620     /**
34621      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34622      */
34623     /**
34624      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34625      */
34626     /**
34627      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34628      */
34629     /**
34630      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34631      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34632      * default renderer uses the raw data value.
34633      */
34634        /**
34635      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34636      */
34637     /**
34638      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34639      */
34640
34641     /**
34642      * Returns the id of the column at the specified index.
34643      * @param {Number} index The column index
34644      * @return {String} the id
34645      */
34646     getColumnId : function(index){
34647         return this.config[index].id;
34648     },
34649
34650     /**
34651      * Returns the column for a specified id.
34652      * @param {String} id The column id
34653      * @return {Object} the column
34654      */
34655     getColumnById : function(id){
34656         return this.lookup[id];
34657     },
34658
34659     
34660     /**
34661      * Returns the column for a specified dataIndex.
34662      * @param {String} dataIndex The column dataIndex
34663      * @return {Object|Boolean} the column or false if not found
34664      */
34665     getColumnByDataIndex: function(dataIndex){
34666         var index = this.findColumnIndex(dataIndex);
34667         return index > -1 ? this.config[index] : false;
34668     },
34669     
34670     /**
34671      * Returns the index for a specified column id.
34672      * @param {String} id The column id
34673      * @return {Number} the index, or -1 if not found
34674      */
34675     getIndexById : function(id){
34676         for(var i = 0, len = this.config.length; i < len; i++){
34677             if(this.config[i].id == id){
34678                 return i;
34679             }
34680         }
34681         return -1;
34682     },
34683     
34684     /**
34685      * Returns the index for a specified column dataIndex.
34686      * @param {String} dataIndex The column dataIndex
34687      * @return {Number} the index, or -1 if not found
34688      */
34689     
34690     findColumnIndex : function(dataIndex){
34691         for(var i = 0, len = this.config.length; i < len; i++){
34692             if(this.config[i].dataIndex == dataIndex){
34693                 return i;
34694             }
34695         }
34696         return -1;
34697     },
34698     
34699     
34700     moveColumn : function(oldIndex, newIndex){
34701         var c = this.config[oldIndex];
34702         this.config.splice(oldIndex, 1);
34703         this.config.splice(newIndex, 0, c);
34704         this.dataMap = null;
34705         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34706     },
34707
34708     isLocked : function(colIndex){
34709         return this.config[colIndex].locked === true;
34710     },
34711
34712     setLocked : function(colIndex, value, suppressEvent){
34713         if(this.isLocked(colIndex) == value){
34714             return;
34715         }
34716         this.config[colIndex].locked = value;
34717         if(!suppressEvent){
34718             this.fireEvent("columnlockchange", this, colIndex, value);
34719         }
34720     },
34721
34722     getTotalLockedWidth : function(){
34723         var totalWidth = 0;
34724         for(var i = 0; i < this.config.length; i++){
34725             if(this.isLocked(i) && !this.isHidden(i)){
34726                 this.totalWidth += this.getColumnWidth(i);
34727             }
34728         }
34729         return totalWidth;
34730     },
34731
34732     getLockedCount : function(){
34733         for(var i = 0, len = this.config.length; i < len; i++){
34734             if(!this.isLocked(i)){
34735                 return i;
34736             }
34737         }
34738     },
34739
34740     /**
34741      * Returns the number of columns.
34742      * @return {Number}
34743      */
34744     getColumnCount : function(visibleOnly){
34745         if(visibleOnly === true){
34746             var c = 0;
34747             for(var i = 0, len = this.config.length; i < len; i++){
34748                 if(!this.isHidden(i)){
34749                     c++;
34750                 }
34751             }
34752             return c;
34753         }
34754         return this.config.length;
34755     },
34756
34757     /**
34758      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34759      * @param {Function} fn
34760      * @param {Object} scope (optional)
34761      * @return {Array} result
34762      */
34763     getColumnsBy : function(fn, scope){
34764         var r = [];
34765         for(var i = 0, len = this.config.length; i < len; i++){
34766             var c = this.config[i];
34767             if(fn.call(scope||this, c, i) === true){
34768                 r[r.length] = c;
34769             }
34770         }
34771         return r;
34772     },
34773
34774     /**
34775      * Returns true if the specified column is sortable.
34776      * @param {Number} col The column index
34777      * @return {Boolean}
34778      */
34779     isSortable : function(col){
34780         if(typeof this.config[col].sortable == "undefined"){
34781             return this.defaultSortable;
34782         }
34783         return this.config[col].sortable;
34784     },
34785
34786     /**
34787      * Returns the rendering (formatting) function defined for the column.
34788      * @param {Number} col The column index.
34789      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34790      */
34791     getRenderer : function(col){
34792         if(!this.config[col].renderer){
34793             return Roo.grid.ColumnModel.defaultRenderer;
34794         }
34795         return this.config[col].renderer;
34796     },
34797
34798     /**
34799      * Sets the rendering (formatting) function for a column.
34800      * @param {Number} col The column index
34801      * @param {Function} fn The function to use to process the cell's raw data
34802      * to return HTML markup for the grid view. The render function is called with
34803      * the following parameters:<ul>
34804      * <li>Data value.</li>
34805      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34806      * <li>css A CSS style string to apply to the table cell.</li>
34807      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34808      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34809      * <li>Row index</li>
34810      * <li>Column index</li>
34811      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34812      */
34813     setRenderer : function(col, fn){
34814         this.config[col].renderer = fn;
34815     },
34816
34817     /**
34818      * Returns the width for the specified column.
34819      * @param {Number} col The column index
34820      * @return {Number}
34821      */
34822     getColumnWidth : function(col){
34823         return this.config[col].width || this.defaultWidth;
34824     },
34825
34826     /**
34827      * Sets the width for a column.
34828      * @param {Number} col The column index
34829      * @param {Number} width The new width
34830      */
34831     setColumnWidth : function(col, width, suppressEvent){
34832         this.config[col].width = width;
34833         this.totalWidth = null;
34834         if(!suppressEvent){
34835              this.fireEvent("widthchange", this, col, width);
34836         }
34837     },
34838
34839     /**
34840      * Returns the total width of all columns.
34841      * @param {Boolean} includeHidden True to include hidden column widths
34842      * @return {Number}
34843      */
34844     getTotalWidth : function(includeHidden){
34845         if(!this.totalWidth){
34846             this.totalWidth = 0;
34847             for(var i = 0, len = this.config.length; i < len; i++){
34848                 if(includeHidden || !this.isHidden(i)){
34849                     this.totalWidth += this.getColumnWidth(i);
34850                 }
34851             }
34852         }
34853         return this.totalWidth;
34854     },
34855
34856     /**
34857      * Returns the header for the specified column.
34858      * @param {Number} col The column index
34859      * @return {String}
34860      */
34861     getColumnHeader : function(col){
34862         return this.config[col].header;
34863     },
34864
34865     /**
34866      * Sets the header for a column.
34867      * @param {Number} col The column index
34868      * @param {String} header The new header
34869      */
34870     setColumnHeader : function(col, header){
34871         this.config[col].header = header;
34872         this.fireEvent("headerchange", this, col, header);
34873     },
34874
34875     /**
34876      * Returns the tooltip for the specified column.
34877      * @param {Number} col The column index
34878      * @return {String}
34879      */
34880     getColumnTooltip : function(col){
34881             return this.config[col].tooltip;
34882     },
34883     /**
34884      * Sets the tooltip for a column.
34885      * @param {Number} col The column index
34886      * @param {String} tooltip The new tooltip
34887      */
34888     setColumnTooltip : function(col, tooltip){
34889             this.config[col].tooltip = tooltip;
34890     },
34891
34892     /**
34893      * Returns the dataIndex for the specified column.
34894      * @param {Number} col The column index
34895      * @return {Number}
34896      */
34897     getDataIndex : function(col){
34898         return this.config[col].dataIndex;
34899     },
34900
34901     /**
34902      * Sets the dataIndex for a column.
34903      * @param {Number} col The column index
34904      * @param {Number} dataIndex The new dataIndex
34905      */
34906     setDataIndex : function(col, dataIndex){
34907         this.config[col].dataIndex = dataIndex;
34908     },
34909
34910     
34911     
34912     /**
34913      * Returns true if the cell is editable.
34914      * @param {Number} colIndex The column index
34915      * @param {Number} rowIndex The row index
34916      * @return {Boolean}
34917      */
34918     isCellEditable : function(colIndex, rowIndex){
34919         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
34920     },
34921
34922     /**
34923      * Returns the editor defined for the cell/column.
34924      * return false or null to disable editing.
34925      * @param {Number} colIndex The column index
34926      * @param {Number} rowIndex The row index
34927      * @return {Object}
34928      */
34929     getCellEditor : function(colIndex, rowIndex){
34930         return this.config[colIndex].editor;
34931     },
34932
34933     /**
34934      * Sets if a column is editable.
34935      * @param {Number} col The column index
34936      * @param {Boolean} editable True if the column is editable
34937      */
34938     setEditable : function(col, editable){
34939         this.config[col].editable = editable;
34940     },
34941
34942
34943     /**
34944      * Returns true if the column is hidden.
34945      * @param {Number} colIndex The column index
34946      * @return {Boolean}
34947      */
34948     isHidden : function(colIndex){
34949         return this.config[colIndex].hidden;
34950     },
34951
34952
34953     /**
34954      * Returns true if the column width cannot be changed
34955      */
34956     isFixed : function(colIndex){
34957         return this.config[colIndex].fixed;
34958     },
34959
34960     /**
34961      * Returns true if the column can be resized
34962      * @return {Boolean}
34963      */
34964     isResizable : function(colIndex){
34965         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
34966     },
34967     /**
34968      * Sets if a column is hidden.
34969      * @param {Number} colIndex The column index
34970      * @param {Boolean} hidden True if the column is hidden
34971      */
34972     setHidden : function(colIndex, hidden){
34973         this.config[colIndex].hidden = hidden;
34974         this.totalWidth = null;
34975         this.fireEvent("hiddenchange", this, colIndex, hidden);
34976     },
34977
34978     /**
34979      * Sets the editor for a column.
34980      * @param {Number} col The column index
34981      * @param {Object} editor The editor object
34982      */
34983     setEditor : function(col, editor){
34984         this.config[col].editor = editor;
34985     }
34986 });
34987
34988 Roo.grid.ColumnModel.defaultRenderer = function(value){
34989         if(typeof value == "string" && value.length < 1){
34990             return "&#160;";
34991         }
34992         return value;
34993 };
34994
34995 // Alias for backwards compatibility
34996 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
34997 /*
34998  * Based on:
34999  * Ext JS Library 1.1.1
35000  * Copyright(c) 2006-2007, Ext JS, LLC.
35001  *
35002  * Originally Released Under LGPL - original licence link has changed is not relivant.
35003  *
35004  * Fork - LGPL
35005  * <script type="text/javascript">
35006  */
35007
35008 /**
35009  * @class Roo.grid.AbstractSelectionModel
35010  * @extends Roo.util.Observable
35011  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35012  * implemented by descendant classes.  This class should not be directly instantiated.
35013  * @constructor
35014  */
35015 Roo.grid.AbstractSelectionModel = function(){
35016     this.locked = false;
35017     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35018 };
35019
35020 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35021     /** @ignore Called by the grid automatically. Do not call directly. */
35022     init : function(grid){
35023         this.grid = grid;
35024         this.initEvents();
35025     },
35026
35027     /**
35028      * Locks the selections.
35029      */
35030     lock : function(){
35031         this.locked = true;
35032     },
35033
35034     /**
35035      * Unlocks the selections.
35036      */
35037     unlock : function(){
35038         this.locked = false;
35039     },
35040
35041     /**
35042      * Returns true if the selections are locked.
35043      * @return {Boolean}
35044      */
35045     isLocked : function(){
35046         return this.locked;
35047     }
35048 });/*
35049  * Based on:
35050  * Ext JS Library 1.1.1
35051  * Copyright(c) 2006-2007, Ext JS, LLC.
35052  *
35053  * Originally Released Under LGPL - original licence link has changed is not relivant.
35054  *
35055  * Fork - LGPL
35056  * <script type="text/javascript">
35057  */
35058 /**
35059  * @extends Roo.grid.AbstractSelectionModel
35060  * @class Roo.grid.RowSelectionModel
35061  * The default SelectionModel used by {@link Roo.grid.Grid}.
35062  * It supports multiple selections and keyboard selection/navigation. 
35063  * @constructor
35064  * @param {Object} config
35065  */
35066 Roo.grid.RowSelectionModel = function(config){
35067     Roo.apply(this, config);
35068     this.selections = new Roo.util.MixedCollection(false, function(o){
35069         return o.id;
35070     });
35071
35072     this.last = false;
35073     this.lastActive = false;
35074
35075     this.addEvents({
35076         /**
35077              * @event selectionchange
35078              * Fires when the selection changes
35079              * @param {SelectionModel} this
35080              */
35081             "selectionchange" : true,
35082         /**
35083              * @event afterselectionchange
35084              * Fires after the selection changes (eg. by key press or clicking)
35085              * @param {SelectionModel} this
35086              */
35087             "afterselectionchange" : true,
35088         /**
35089              * @event beforerowselect
35090              * Fires when a row is selected being selected, return false to cancel.
35091              * @param {SelectionModel} this
35092              * @param {Number} rowIndex The selected index
35093              * @param {Boolean} keepExisting False if other selections will be cleared
35094              */
35095             "beforerowselect" : true,
35096         /**
35097              * @event rowselect
35098              * Fires when a row is selected.
35099              * @param {SelectionModel} this
35100              * @param {Number} rowIndex The selected index
35101              * @param {Roo.data.Record} r The record
35102              */
35103             "rowselect" : true,
35104         /**
35105              * @event rowdeselect
35106              * Fires when a row is deselected.
35107              * @param {SelectionModel} this
35108              * @param {Number} rowIndex The selected index
35109              */
35110         "rowdeselect" : true
35111     });
35112     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35113     this.locked = false;
35114 };
35115
35116 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35117     /**
35118      * @cfg {Boolean} singleSelect
35119      * True to allow selection of only one row at a time (defaults to false)
35120      */
35121     singleSelect : false,
35122
35123     // private
35124     initEvents : function(){
35125
35126         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35127             this.grid.on("mousedown", this.handleMouseDown, this);
35128         }else{ // allow click to work like normal
35129             this.grid.on("rowclick", this.handleDragableRowClick, this);
35130         }
35131
35132         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35133             "up" : function(e){
35134                 if(!e.shiftKey){
35135                     this.selectPrevious(e.shiftKey);
35136                 }else if(this.last !== false && this.lastActive !== false){
35137                     var last = this.last;
35138                     this.selectRange(this.last,  this.lastActive-1);
35139                     this.grid.getView().focusRow(this.lastActive);
35140                     if(last !== false){
35141                         this.last = last;
35142                     }
35143                 }else{
35144                     this.selectFirstRow();
35145                 }
35146                 this.fireEvent("afterselectionchange", this);
35147             },
35148             "down" : function(e){
35149                 if(!e.shiftKey){
35150                     this.selectNext(e.shiftKey);
35151                 }else if(this.last !== false && this.lastActive !== false){
35152                     var last = this.last;
35153                     this.selectRange(this.last,  this.lastActive+1);
35154                     this.grid.getView().focusRow(this.lastActive);
35155                     if(last !== false){
35156                         this.last = last;
35157                     }
35158                 }else{
35159                     this.selectFirstRow();
35160                 }
35161                 this.fireEvent("afterselectionchange", this);
35162             },
35163             scope: this
35164         });
35165
35166         var view = this.grid.view;
35167         view.on("refresh", this.onRefresh, this);
35168         view.on("rowupdated", this.onRowUpdated, this);
35169         view.on("rowremoved", this.onRemove, this);
35170     },
35171
35172     // private
35173     onRefresh : function(){
35174         var ds = this.grid.dataSource, i, v = this.grid.view;
35175         var s = this.selections;
35176         s.each(function(r){
35177             if((i = ds.indexOfId(r.id)) != -1){
35178                 v.onRowSelect(i);
35179             }else{
35180                 s.remove(r);
35181             }
35182         });
35183     },
35184
35185     // private
35186     onRemove : function(v, index, r){
35187         this.selections.remove(r);
35188     },
35189
35190     // private
35191     onRowUpdated : function(v, index, r){
35192         if(this.isSelected(r)){
35193             v.onRowSelect(index);
35194         }
35195     },
35196
35197     /**
35198      * Select records.
35199      * @param {Array} records The records to select
35200      * @param {Boolean} keepExisting (optional) True to keep existing selections
35201      */
35202     selectRecords : function(records, keepExisting){
35203         if(!keepExisting){
35204             this.clearSelections();
35205         }
35206         var ds = this.grid.dataSource;
35207         for(var i = 0, len = records.length; i < len; i++){
35208             this.selectRow(ds.indexOf(records[i]), true);
35209         }
35210     },
35211
35212     /**
35213      * Gets the number of selected rows.
35214      * @return {Number}
35215      */
35216     getCount : function(){
35217         return this.selections.length;
35218     },
35219
35220     /**
35221      * Selects the first row in the grid.
35222      */
35223     selectFirstRow : function(){
35224         this.selectRow(0);
35225     },
35226
35227     /**
35228      * Select the last row.
35229      * @param {Boolean} keepExisting (optional) True to keep existing selections
35230      */
35231     selectLastRow : function(keepExisting){
35232         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35233     },
35234
35235     /**
35236      * Selects the row immediately following the last selected row.
35237      * @param {Boolean} keepExisting (optional) True to keep existing selections
35238      */
35239     selectNext : function(keepExisting){
35240         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35241             this.selectRow(this.last+1, keepExisting);
35242             this.grid.getView().focusRow(this.last);
35243         }
35244     },
35245
35246     /**
35247      * Selects the row that precedes the last selected row.
35248      * @param {Boolean} keepExisting (optional) True to keep existing selections
35249      */
35250     selectPrevious : function(keepExisting){
35251         if(this.last){
35252             this.selectRow(this.last-1, keepExisting);
35253             this.grid.getView().focusRow(this.last);
35254         }
35255     },
35256
35257     /**
35258      * Returns the selected records
35259      * @return {Array} Array of selected records
35260      */
35261     getSelections : function(){
35262         return [].concat(this.selections.items);
35263     },
35264
35265     /**
35266      * Returns the first selected record.
35267      * @return {Record}
35268      */
35269     getSelected : function(){
35270         return this.selections.itemAt(0);
35271     },
35272
35273
35274     /**
35275      * Clears all selections.
35276      */
35277     clearSelections : function(fast){
35278         if(this.locked) return;
35279         if(fast !== true){
35280             var ds = this.grid.dataSource;
35281             var s = this.selections;
35282             s.each(function(r){
35283                 this.deselectRow(ds.indexOfId(r.id));
35284             }, this);
35285             s.clear();
35286         }else{
35287             this.selections.clear();
35288         }
35289         this.last = false;
35290     },
35291
35292
35293     /**
35294      * Selects all rows.
35295      */
35296     selectAll : function(){
35297         if(this.locked) return;
35298         this.selections.clear();
35299         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35300             this.selectRow(i, true);
35301         }
35302     },
35303
35304     /**
35305      * Returns True if there is a selection.
35306      * @return {Boolean}
35307      */
35308     hasSelection : function(){
35309         return this.selections.length > 0;
35310     },
35311
35312     /**
35313      * Returns True if the specified row is selected.
35314      * @param {Number/Record} record The record or index of the record to check
35315      * @return {Boolean}
35316      */
35317     isSelected : function(index){
35318         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35319         return (r && this.selections.key(r.id) ? true : false);
35320     },
35321
35322     /**
35323      * Returns True if the specified record id is selected.
35324      * @param {String} id The id of record to check
35325      * @return {Boolean}
35326      */
35327     isIdSelected : function(id){
35328         return (this.selections.key(id) ? true : false);
35329     },
35330
35331     // private
35332     handleMouseDown : function(e, t){
35333         var view = this.grid.getView(), rowIndex;
35334         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35335             return;
35336         };
35337         if(e.shiftKey && this.last !== false){
35338             var last = this.last;
35339             this.selectRange(last, rowIndex, e.ctrlKey);
35340             this.last = last; // reset the last
35341             view.focusRow(rowIndex);
35342         }else{
35343             var isSelected = this.isSelected(rowIndex);
35344             if(e.button !== 0 && isSelected){
35345                 view.focusRow(rowIndex);
35346             }else if(e.ctrlKey && isSelected){
35347                 this.deselectRow(rowIndex);
35348             }else if(!isSelected){
35349                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35350                 view.focusRow(rowIndex);
35351             }
35352         }
35353         this.fireEvent("afterselectionchange", this);
35354     },
35355     // private
35356     handleDragableRowClick :  function(grid, rowIndex, e) 
35357     {
35358         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35359             this.selectRow(rowIndex, false);
35360             grid.view.focusRow(rowIndex);
35361              this.fireEvent("afterselectionchange", this);
35362         }
35363     },
35364     
35365     /**
35366      * Selects multiple rows.
35367      * @param {Array} rows Array of the indexes of the row to select
35368      * @param {Boolean} keepExisting (optional) True to keep existing selections
35369      */
35370     selectRows : function(rows, keepExisting){
35371         if(!keepExisting){
35372             this.clearSelections();
35373         }
35374         for(var i = 0, len = rows.length; i < len; i++){
35375             this.selectRow(rows[i], true);
35376         }
35377     },
35378
35379     /**
35380      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35381      * @param {Number} startRow The index of the first row in the range
35382      * @param {Number} endRow The index of the last row in the range
35383      * @param {Boolean} keepExisting (optional) True to retain existing selections
35384      */
35385     selectRange : function(startRow, endRow, keepExisting){
35386         if(this.locked) return;
35387         if(!keepExisting){
35388             this.clearSelections();
35389         }
35390         if(startRow <= endRow){
35391             for(var i = startRow; i <= endRow; i++){
35392                 this.selectRow(i, true);
35393             }
35394         }else{
35395             for(var i = startRow; i >= endRow; i--){
35396                 this.selectRow(i, true);
35397             }
35398         }
35399     },
35400
35401     /**
35402      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35403      * @param {Number} startRow The index of the first row in the range
35404      * @param {Number} endRow The index of the last row in the range
35405      */
35406     deselectRange : function(startRow, endRow, preventViewNotify){
35407         if(this.locked) return;
35408         for(var i = startRow; i <= endRow; i++){
35409             this.deselectRow(i, preventViewNotify);
35410         }
35411     },
35412
35413     /**
35414      * Selects a row.
35415      * @param {Number} row The index of the row to select
35416      * @param {Boolean} keepExisting (optional) True to keep existing selections
35417      */
35418     selectRow : function(index, keepExisting, preventViewNotify){
35419         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
35420         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35421             if(!keepExisting || this.singleSelect){
35422                 this.clearSelections();
35423             }
35424             var r = this.grid.dataSource.getAt(index);
35425             this.selections.add(r);
35426             this.last = this.lastActive = index;
35427             if(!preventViewNotify){
35428                 this.grid.getView().onRowSelect(index);
35429             }
35430             this.fireEvent("rowselect", this, index, r);
35431             this.fireEvent("selectionchange", this);
35432         }
35433     },
35434
35435     /**
35436      * Deselects a row.
35437      * @param {Number} row The index of the row to deselect
35438      */
35439     deselectRow : function(index, preventViewNotify){
35440         if(this.locked) return;
35441         if(this.last == index){
35442             this.last = false;
35443         }
35444         if(this.lastActive == index){
35445             this.lastActive = false;
35446         }
35447         var r = this.grid.dataSource.getAt(index);
35448         this.selections.remove(r);
35449         if(!preventViewNotify){
35450             this.grid.getView().onRowDeselect(index);
35451         }
35452         this.fireEvent("rowdeselect", this, index);
35453         this.fireEvent("selectionchange", this);
35454     },
35455
35456     // private
35457     restoreLast : function(){
35458         if(this._last){
35459             this.last = this._last;
35460         }
35461     },
35462
35463     // private
35464     acceptsNav : function(row, col, cm){
35465         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35466     },
35467
35468     // private
35469     onEditorKey : function(field, e){
35470         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35471         if(k == e.TAB){
35472             e.stopEvent();
35473             ed.completeEdit();
35474             if(e.shiftKey){
35475                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35476             }else{
35477                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35478             }
35479         }else if(k == e.ENTER && !e.ctrlKey){
35480             e.stopEvent();
35481             ed.completeEdit();
35482             if(e.shiftKey){
35483                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35484             }else{
35485                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35486             }
35487         }else if(k == e.ESC){
35488             ed.cancelEdit();
35489         }
35490         if(newCell){
35491             g.startEditing(newCell[0], newCell[1]);
35492         }
35493     }
35494 });/*
35495  * Based on:
35496  * Ext JS Library 1.1.1
35497  * Copyright(c) 2006-2007, Ext JS, LLC.
35498  *
35499  * Originally Released Under LGPL - original licence link has changed is not relivant.
35500  *
35501  * Fork - LGPL
35502  * <script type="text/javascript">
35503  */
35504 /**
35505  * @class Roo.grid.CellSelectionModel
35506  * @extends Roo.grid.AbstractSelectionModel
35507  * This class provides the basic implementation for cell selection in a grid.
35508  * @constructor
35509  * @param {Object} config The object containing the configuration of this model.
35510  */
35511 Roo.grid.CellSelectionModel = function(config){
35512     Roo.apply(this, config);
35513
35514     this.selection = null;
35515
35516     this.addEvents({
35517         /**
35518              * @event beforerowselect
35519              * Fires before a cell is selected.
35520              * @param {SelectionModel} this
35521              * @param {Number} rowIndex The selected row index
35522              * @param {Number} colIndex The selected cell index
35523              */
35524             "beforecellselect" : true,
35525         /**
35526              * @event cellselect
35527              * Fires when a cell is selected.
35528              * @param {SelectionModel} this
35529              * @param {Number} rowIndex The selected row index
35530              * @param {Number} colIndex The selected cell index
35531              */
35532             "cellselect" : true,
35533         /**
35534              * @event selectionchange
35535              * Fires when the active selection changes.
35536              * @param {SelectionModel} this
35537              * @param {Object} selection null for no selection or an object (o) with two properties
35538                 <ul>
35539                 <li>o.record: the record object for the row the selection is in</li>
35540                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35541                 </ul>
35542              */
35543             "selectionchange" : true
35544     });
35545     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35546 };
35547
35548 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35549
35550     /** @ignore */
35551     initEvents : function(){
35552         this.grid.on("mousedown", this.handleMouseDown, this);
35553         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35554         var view = this.grid.view;
35555         view.on("refresh", this.onViewChange, this);
35556         view.on("rowupdated", this.onRowUpdated, this);
35557         view.on("beforerowremoved", this.clearSelections, this);
35558         view.on("beforerowsinserted", this.clearSelections, this);
35559         if(this.grid.isEditor){
35560             this.grid.on("beforeedit", this.beforeEdit,  this);
35561         }
35562     },
35563
35564         //private
35565     beforeEdit : function(e){
35566         this.select(e.row, e.column, false, true, e.record);
35567     },
35568
35569         //private
35570     onRowUpdated : function(v, index, r){
35571         if(this.selection && this.selection.record == r){
35572             v.onCellSelect(index, this.selection.cell[1]);
35573         }
35574     },
35575
35576         //private
35577     onViewChange : function(){
35578         this.clearSelections(true);
35579     },
35580
35581         /**
35582          * Returns the currently selected cell,.
35583          * @return {Array} The selected cell (row, column) or null if none selected.
35584          */
35585     getSelectedCell : function(){
35586         return this.selection ? this.selection.cell : null;
35587     },
35588
35589     /**
35590      * Clears all selections.
35591      * @param {Boolean} true to prevent the gridview from being notified about the change.
35592      */
35593     clearSelections : function(preventNotify){
35594         var s = this.selection;
35595         if(s){
35596             if(preventNotify !== true){
35597                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35598             }
35599             this.selection = null;
35600             this.fireEvent("selectionchange", this, null);
35601         }
35602     },
35603
35604     /**
35605      * Returns true if there is a selection.
35606      * @return {Boolean}
35607      */
35608     hasSelection : function(){
35609         return this.selection ? true : false;
35610     },
35611
35612     /** @ignore */
35613     handleMouseDown : function(e, t){
35614         var v = this.grid.getView();
35615         if(this.isLocked()){
35616             return;
35617         };
35618         var row = v.findRowIndex(t);
35619         var cell = v.findCellIndex(t);
35620         if(row !== false && cell !== false){
35621             this.select(row, cell);
35622         }
35623     },
35624
35625     /**
35626      * Selects a cell.
35627      * @param {Number} rowIndex
35628      * @param {Number} collIndex
35629      */
35630     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35631         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35632             this.clearSelections();
35633             r = r || this.grid.dataSource.getAt(rowIndex);
35634             this.selection = {
35635                 record : r,
35636                 cell : [rowIndex, colIndex]
35637             };
35638             if(!preventViewNotify){
35639                 var v = this.grid.getView();
35640                 v.onCellSelect(rowIndex, colIndex);
35641                 if(preventFocus !== true){
35642                     v.focusCell(rowIndex, colIndex);
35643                 }
35644             }
35645             this.fireEvent("cellselect", this, rowIndex, colIndex);
35646             this.fireEvent("selectionchange", this, this.selection);
35647         }
35648     },
35649
35650         //private
35651     isSelectable : function(rowIndex, colIndex, cm){
35652         return !cm.isHidden(colIndex);
35653     },
35654
35655     /** @ignore */
35656     handleKeyDown : function(e){
35657         Roo.log('Cell Sel Model handleKeyDown');
35658         if(!e.isNavKeyPress()){
35659             return;
35660         }
35661         var g = this.grid, s = this.selection;
35662         if(!s){
35663             e.stopEvent();
35664             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35665             if(cell){
35666                 this.select(cell[0], cell[1]);
35667             }
35668             return;
35669         }
35670         var sm = this;
35671         var walk = function(row, col, step){
35672             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35673         };
35674         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35675         var newCell;
35676
35677         switch(k){
35678             case e.TAB:
35679                 // handled by onEditorKey
35680                 if (g.isEditor && g.editing) {
35681                     return;
35682                 }
35683                 if(e.shiftKey){
35684                      newCell = walk(r, c-1, -1);
35685                 }else{
35686                      newCell = walk(r, c+1, 1);
35687                 }
35688              break;
35689              case e.DOWN:
35690                  newCell = walk(r+1, c, 1);
35691              break;
35692              case e.UP:
35693                  newCell = walk(r-1, c, -1);
35694              break;
35695              case e.RIGHT:
35696                  newCell = walk(r, c+1, 1);
35697              break;
35698              case e.LEFT:
35699                  newCell = walk(r, c-1, -1);
35700              break;
35701              case e.ENTER:
35702                  if(g.isEditor && !g.editing){
35703                     g.startEditing(r, c);
35704                     e.stopEvent();
35705                     return;
35706                 }
35707              break;
35708         };
35709         if(newCell){
35710             this.select(newCell[0], newCell[1]);
35711             e.stopEvent();
35712         }
35713     },
35714
35715     acceptsNav : function(row, col, cm){
35716         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35717     },
35718
35719     onEditorKey : function(field, e){
35720         
35721         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35722         ///Roo.log('onEditorKey' + k);
35723         
35724         if(k == e.TAB){
35725             if(e.shiftKey){
35726                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35727             }else{
35728                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35729             }
35730             e.stopEvent();
35731         }else if(k == e.ENTER && !e.ctrlKey){
35732             ed.completeEdit();
35733             e.stopEvent();
35734             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35735         }else if(k == e.ESC){
35736             ed.cancelEdit();
35737         }
35738         
35739         
35740         if(newCell){
35741             //Roo.log('next cell after edit');
35742             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
35743         }
35744     }
35745 });/*
35746  * Based on:
35747  * Ext JS Library 1.1.1
35748  * Copyright(c) 2006-2007, Ext JS, LLC.
35749  *
35750  * Originally Released Under LGPL - original licence link has changed is not relivant.
35751  *
35752  * Fork - LGPL
35753  * <script type="text/javascript">
35754  */
35755  
35756 /**
35757  * @class Roo.grid.EditorGrid
35758  * @extends Roo.grid.Grid
35759  * Class for creating and editable grid.
35760  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
35761  * The container MUST have some type of size defined for the grid to fill. The container will be 
35762  * automatically set to position relative if it isn't already.
35763  * @param {Object} dataSource The data model to bind to
35764  * @param {Object} colModel The column model with info about this grid's columns
35765  */
35766 Roo.grid.EditorGrid = function(container, config){
35767     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
35768     this.getGridEl().addClass("xedit-grid");
35769
35770     if(!this.selModel){
35771         this.selModel = new Roo.grid.CellSelectionModel();
35772     }
35773
35774     this.activeEditor = null;
35775
35776         this.addEvents({
35777             /**
35778              * @event beforeedit
35779              * Fires before cell editing is triggered. The edit event object has the following properties <br />
35780              * <ul style="padding:5px;padding-left:16px;">
35781              * <li>grid - This grid</li>
35782              * <li>record - The record being edited</li>
35783              * <li>field - The field name being edited</li>
35784              * <li>value - The value for the field being edited.</li>
35785              * <li>row - The grid row index</li>
35786              * <li>column - The grid column index</li>
35787              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35788              * </ul>
35789              * @param {Object} e An edit event (see above for description)
35790              */
35791             "beforeedit" : true,
35792             /**
35793              * @event afteredit
35794              * Fires after a cell is edited. <br />
35795              * <ul style="padding:5px;padding-left:16px;">
35796              * <li>grid - This grid</li>
35797              * <li>record - The record being edited</li>
35798              * <li>field - The field name being edited</li>
35799              * <li>value - The value being set</li>
35800              * <li>originalValue - The original value for the field, before the edit.</li>
35801              * <li>row - The grid row index</li>
35802              * <li>column - The grid column index</li>
35803              * </ul>
35804              * @param {Object} e An edit event (see above for description)
35805              */
35806             "afteredit" : true,
35807             /**
35808              * @event validateedit
35809              * Fires after a cell is edited, but before the value is set in the record. 
35810          * You can use this to modify the value being set in the field, Return false
35811              * to cancel the change. The edit event object has the following properties <br />
35812              * <ul style="padding:5px;padding-left:16px;">
35813          * <li>editor - This editor</li>
35814              * <li>grid - This grid</li>
35815              * <li>record - The record being edited</li>
35816              * <li>field - The field name being edited</li>
35817              * <li>value - The value being set</li>
35818              * <li>originalValue - The original value for the field, before the edit.</li>
35819              * <li>row - The grid row index</li>
35820              * <li>column - The grid column index</li>
35821              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35822              * </ul>
35823              * @param {Object} e An edit event (see above for description)
35824              */
35825             "validateedit" : true
35826         });
35827     this.on("bodyscroll", this.stopEditing,  this);
35828     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
35829 };
35830
35831 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
35832     /**
35833      * @cfg {Number} clicksToEdit
35834      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
35835      */
35836     clicksToEdit: 2,
35837
35838     // private
35839     isEditor : true,
35840     // private
35841     trackMouseOver: false, // causes very odd FF errors
35842
35843     onCellDblClick : function(g, row, col){
35844         this.startEditing(row, col);
35845     },
35846
35847     onEditComplete : function(ed, value, startValue){
35848         this.editing = false;
35849         this.activeEditor = null;
35850         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
35851         var r = ed.record;
35852         var field = this.colModel.getDataIndex(ed.col);
35853         var e = {
35854             grid: this,
35855             record: r,
35856             field: field,
35857             originalValue: startValue,
35858             value: value,
35859             row: ed.row,
35860             column: ed.col,
35861             cancel:false,
35862             editor: ed
35863         };
35864         if(String(value) !== String(startValue)){
35865             
35866             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
35867                 r.set(field, e.value);
35868                 // if we are dealing with a combo box..
35869                 // then we also set the 'name' colum to be the displayField
35870                 if (ed.field.displayField && ed.field.name) {
35871                     r.set(ed.field.name, ed.field.el.dom.value);
35872                 }
35873                 
35874                 delete e.cancel; //?? why!!!
35875                 this.fireEvent("afteredit", e);
35876             }
35877         } else {
35878             this.fireEvent("afteredit", e); // always fire it!
35879         }
35880         this.view.focusCell(ed.row, ed.col);
35881     },
35882
35883     /**
35884      * Starts editing the specified for the specified row/column
35885      * @param {Number} rowIndex
35886      * @param {Number} colIndex
35887      */
35888     startEditing : function(row, col){
35889         this.stopEditing();
35890         if(this.colModel.isCellEditable(col, row)){
35891             this.view.ensureVisible(row, col, true);
35892             var r = this.dataSource.getAt(row);
35893             var field = this.colModel.getDataIndex(col);
35894             var e = {
35895                 grid: this,
35896                 record: r,
35897                 field: field,
35898                 value: r.data[field],
35899                 row: row,
35900                 column: col,
35901                 cancel:false
35902             };
35903             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
35904                 this.editing = true;
35905                 var ed = this.colModel.getCellEditor(col, row);
35906                 
35907                 if (!ed) {
35908                     return;
35909                 }
35910                 if(!ed.rendered){
35911                     ed.render(ed.parentEl || document.body);
35912                 }
35913                 ed.field.reset();
35914                 (function(){ // complex but required for focus issues in safari, ie and opera
35915                     ed.row = row;
35916                     ed.col = col;
35917                     ed.record = r;
35918                     ed.on("complete", this.onEditComplete, this, {single: true});
35919                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
35920                     this.activeEditor = ed;
35921                     var v = r.data[field];
35922                     ed.startEdit(this.view.getCell(row, col), v);
35923                     // combo's with 'displayField and name set
35924                     if (ed.field.displayField && ed.field.name) {
35925                         ed.field.el.dom.value = r.data[ed.field.name];
35926                     }
35927                     
35928                     
35929                 }).defer(50, this);
35930             }
35931         }
35932     },
35933         
35934     /**
35935      * Stops any active editing
35936      */
35937     stopEditing : function(){
35938         if(this.activeEditor){
35939             this.activeEditor.completeEdit();
35940         }
35941         this.activeEditor = null;
35942     }
35943 });/*
35944  * Based on:
35945  * Ext JS Library 1.1.1
35946  * Copyright(c) 2006-2007, Ext JS, LLC.
35947  *
35948  * Originally Released Under LGPL - original licence link has changed is not relivant.
35949  *
35950  * Fork - LGPL
35951  * <script type="text/javascript">
35952  */
35953
35954 // private - not really -- you end up using it !
35955 // This is a support class used internally by the Grid components
35956
35957 /**
35958  * @class Roo.grid.GridEditor
35959  * @extends Roo.Editor
35960  * Class for creating and editable grid elements.
35961  * @param {Object} config any settings (must include field)
35962  */
35963 Roo.grid.GridEditor = function(field, config){
35964     if (!config && field.field) {
35965         config = field;
35966         field = Roo.factory(config.field, Roo.form);
35967     }
35968     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
35969     field.monitorTab = false;
35970 };
35971
35972 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
35973     
35974     /**
35975      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
35976      */
35977     
35978     alignment: "tl-tl",
35979     autoSize: "width",
35980     hideEl : false,
35981     cls: "x-small-editor x-grid-editor",
35982     shim:false,
35983     shadow:"frame"
35984 });/*
35985  * Based on:
35986  * Ext JS Library 1.1.1
35987  * Copyright(c) 2006-2007, Ext JS, LLC.
35988  *
35989  * Originally Released Under LGPL - original licence link has changed is not relivant.
35990  *
35991  * Fork - LGPL
35992  * <script type="text/javascript">
35993  */
35994   
35995
35996   
35997 Roo.grid.PropertyRecord = Roo.data.Record.create([
35998     {name:'name',type:'string'},  'value'
35999 ]);
36000
36001
36002 Roo.grid.PropertyStore = function(grid, source){
36003     this.grid = grid;
36004     this.store = new Roo.data.Store({
36005         recordType : Roo.grid.PropertyRecord
36006     });
36007     this.store.on('update', this.onUpdate,  this);
36008     if(source){
36009         this.setSource(source);
36010     }
36011     Roo.grid.PropertyStore.superclass.constructor.call(this);
36012 };
36013
36014
36015
36016 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36017     setSource : function(o){
36018         this.source = o;
36019         this.store.removeAll();
36020         var data = [];
36021         for(var k in o){
36022             if(this.isEditableValue(o[k])){
36023                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36024             }
36025         }
36026         this.store.loadRecords({records: data}, {}, true);
36027     },
36028
36029     onUpdate : function(ds, record, type){
36030         if(type == Roo.data.Record.EDIT){
36031             var v = record.data['value'];
36032             var oldValue = record.modified['value'];
36033             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36034                 this.source[record.id] = v;
36035                 record.commit();
36036                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36037             }else{
36038                 record.reject();
36039             }
36040         }
36041     },
36042
36043     getProperty : function(row){
36044        return this.store.getAt(row);
36045     },
36046
36047     isEditableValue: function(val){
36048         if(val && val instanceof Date){
36049             return true;
36050         }else if(typeof val == 'object' || typeof val == 'function'){
36051             return false;
36052         }
36053         return true;
36054     },
36055
36056     setValue : function(prop, value){
36057         this.source[prop] = value;
36058         this.store.getById(prop).set('value', value);
36059     },
36060
36061     getSource : function(){
36062         return this.source;
36063     }
36064 });
36065
36066 Roo.grid.PropertyColumnModel = function(grid, store){
36067     this.grid = grid;
36068     var g = Roo.grid;
36069     g.PropertyColumnModel.superclass.constructor.call(this, [
36070         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36071         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36072     ]);
36073     this.store = store;
36074     this.bselect = Roo.DomHelper.append(document.body, {
36075         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36076             {tag: 'option', value: 'true', html: 'true'},
36077             {tag: 'option', value: 'false', html: 'false'}
36078         ]
36079     });
36080     Roo.id(this.bselect);
36081     var f = Roo.form;
36082     this.editors = {
36083         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36084         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36085         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36086         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36087         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36088     };
36089     this.renderCellDelegate = this.renderCell.createDelegate(this);
36090     this.renderPropDelegate = this.renderProp.createDelegate(this);
36091 };
36092
36093 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36094     
36095     
36096     nameText : 'Name',
36097     valueText : 'Value',
36098     
36099     dateFormat : 'm/j/Y',
36100     
36101     
36102     renderDate : function(dateVal){
36103         return dateVal.dateFormat(this.dateFormat);
36104     },
36105
36106     renderBool : function(bVal){
36107         return bVal ? 'true' : 'false';
36108     },
36109
36110     isCellEditable : function(colIndex, rowIndex){
36111         return colIndex == 1;
36112     },
36113
36114     getRenderer : function(col){
36115         return col == 1 ?
36116             this.renderCellDelegate : this.renderPropDelegate;
36117     },
36118
36119     renderProp : function(v){
36120         return this.getPropertyName(v);
36121     },
36122
36123     renderCell : function(val){
36124         var rv = val;
36125         if(val instanceof Date){
36126             rv = this.renderDate(val);
36127         }else if(typeof val == 'boolean'){
36128             rv = this.renderBool(val);
36129         }
36130         return Roo.util.Format.htmlEncode(rv);
36131     },
36132
36133     getPropertyName : function(name){
36134         var pn = this.grid.propertyNames;
36135         return pn && pn[name] ? pn[name] : name;
36136     },
36137
36138     getCellEditor : function(colIndex, rowIndex){
36139         var p = this.store.getProperty(rowIndex);
36140         var n = p.data['name'], val = p.data['value'];
36141         
36142         if(typeof(this.grid.customEditors[n]) == 'string'){
36143             return this.editors[this.grid.customEditors[n]];
36144         }
36145         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36146             return this.grid.customEditors[n];
36147         }
36148         if(val instanceof Date){
36149             return this.editors['date'];
36150         }else if(typeof val == 'number'){
36151             return this.editors['number'];
36152         }else if(typeof val == 'boolean'){
36153             return this.editors['boolean'];
36154         }else{
36155             return this.editors['string'];
36156         }
36157     }
36158 });
36159
36160 /**
36161  * @class Roo.grid.PropertyGrid
36162  * @extends Roo.grid.EditorGrid
36163  * This class represents the  interface of a component based property grid control.
36164  * <br><br>Usage:<pre><code>
36165  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36166       
36167  });
36168  // set any options
36169  grid.render();
36170  * </code></pre>
36171   
36172  * @constructor
36173  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36174  * The container MUST have some type of size defined for the grid to fill. The container will be
36175  * automatically set to position relative if it isn't already.
36176  * @param {Object} config A config object that sets properties on this grid.
36177  */
36178 Roo.grid.PropertyGrid = function(container, config){
36179     config = config || {};
36180     var store = new Roo.grid.PropertyStore(this);
36181     this.store = store;
36182     var cm = new Roo.grid.PropertyColumnModel(this, store);
36183     store.store.sort('name', 'ASC');
36184     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36185         ds: store.store,
36186         cm: cm,
36187         enableColLock:false,
36188         enableColumnMove:false,
36189         stripeRows:false,
36190         trackMouseOver: false,
36191         clicksToEdit:1
36192     }, config));
36193     this.getGridEl().addClass('x-props-grid');
36194     this.lastEditRow = null;
36195     this.on('columnresize', this.onColumnResize, this);
36196     this.addEvents({
36197          /**
36198              * @event beforepropertychange
36199              * Fires before a property changes (return false to stop?)
36200              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36201              * @param {String} id Record Id
36202              * @param {String} newval New Value
36203          * @param {String} oldval Old Value
36204              */
36205         "beforepropertychange": true,
36206         /**
36207              * @event propertychange
36208              * Fires after a property changes
36209              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36210              * @param {String} id Record Id
36211              * @param {String} newval New Value
36212          * @param {String} oldval Old Value
36213              */
36214         "propertychange": true
36215     });
36216     this.customEditors = this.customEditors || {};
36217 };
36218 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36219     
36220      /**
36221      * @cfg {Object} customEditors map of colnames=> custom editors.
36222      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36223      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36224      * false disables editing of the field.
36225          */
36226     
36227       /**
36228      * @cfg {Object} propertyNames map of property Names to their displayed value
36229          */
36230     
36231     render : function(){
36232         Roo.grid.PropertyGrid.superclass.render.call(this);
36233         this.autoSize.defer(100, this);
36234     },
36235
36236     autoSize : function(){
36237         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36238         if(this.view){
36239             this.view.fitColumns();
36240         }
36241     },
36242
36243     onColumnResize : function(){
36244         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36245         this.autoSize();
36246     },
36247     /**
36248      * Sets the data for the Grid
36249      * accepts a Key => Value object of all the elements avaiable.
36250      * @param {Object} data  to appear in grid.
36251      */
36252     setSource : function(source){
36253         this.store.setSource(source);
36254         //this.autoSize();
36255     },
36256     /**
36257      * Gets all the data from the grid.
36258      * @return {Object} data  data stored in grid
36259      */
36260     getSource : function(){
36261         return this.store.getSource();
36262     }
36263 });/*
36264  * Based on:
36265  * Ext JS Library 1.1.1
36266  * Copyright(c) 2006-2007, Ext JS, LLC.
36267  *
36268  * Originally Released Under LGPL - original licence link has changed is not relivant.
36269  *
36270  * Fork - LGPL
36271  * <script type="text/javascript">
36272  */
36273  
36274 /**
36275  * @class Roo.LoadMask
36276  * A simple utility class for generically masking elements while loading data.  If the element being masked has
36277  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
36278  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
36279  * element's UpdateManager load indicator and will be destroyed after the initial load.
36280  * @constructor
36281  * Create a new LoadMask
36282  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
36283  * @param {Object} config The config object
36284  */
36285 Roo.LoadMask = function(el, config){
36286     this.el = Roo.get(el);
36287     Roo.apply(this, config);
36288     if(this.store){
36289         this.store.on('beforeload', this.onBeforeLoad, this);
36290         this.store.on('load', this.onLoad, this);
36291         this.store.on('loadexception', this.onLoad, this);
36292         this.removeMask = false;
36293     }else{
36294         var um = this.el.getUpdateManager();
36295         um.showLoadIndicator = false; // disable the default indicator
36296         um.on('beforeupdate', this.onBeforeLoad, this);
36297         um.on('update', this.onLoad, this);
36298         um.on('failure', this.onLoad, this);
36299         this.removeMask = true;
36300     }
36301 };
36302
36303 Roo.LoadMask.prototype = {
36304     /**
36305      * @cfg {Boolean} removeMask
36306      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
36307      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
36308      */
36309     /**
36310      * @cfg {String} msg
36311      * The text to display in a centered loading message box (defaults to 'Loading...')
36312      */
36313     msg : 'Loading...',
36314     /**
36315      * @cfg {String} msgCls
36316      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
36317      */
36318     msgCls : 'x-mask-loading',
36319
36320     /**
36321      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
36322      * @type Boolean
36323      */
36324     disabled: false,
36325
36326     /**
36327      * Disables the mask to prevent it from being displayed
36328      */
36329     disable : function(){
36330        this.disabled = true;
36331     },
36332
36333     /**
36334      * Enables the mask so that it can be displayed
36335      */
36336     enable : function(){
36337         this.disabled = false;
36338     },
36339
36340     // private
36341     onLoad : function(){
36342         this.el.unmask(this.removeMask);
36343     },
36344
36345     // private
36346     onBeforeLoad : function(){
36347         if(!this.disabled){
36348             this.el.mask(this.msg, this.msgCls);
36349         }
36350     },
36351
36352     // private
36353     destroy : function(){
36354         if(this.store){
36355             this.store.un('beforeload', this.onBeforeLoad, this);
36356             this.store.un('load', this.onLoad, this);
36357             this.store.un('loadexception', this.onLoad, this);
36358         }else{
36359             var um = this.el.getUpdateManager();
36360             um.un('beforeupdate', this.onBeforeLoad, this);
36361             um.un('update', this.onLoad, this);
36362             um.un('failure', this.onLoad, this);
36363         }
36364     }
36365 };/*
36366  * Based on:
36367  * Ext JS Library 1.1.1
36368  * Copyright(c) 2006-2007, Ext JS, LLC.
36369  *
36370  * Originally Released Under LGPL - original licence link has changed is not relivant.
36371  *
36372  * Fork - LGPL
36373  * <script type="text/javascript">
36374  */
36375 Roo.XTemplate = function(){
36376     Roo.XTemplate.superclass.constructor.apply(this, arguments);
36377     var s = this.html;
36378
36379     s = ['<tpl>', s, '</tpl>'].join('');
36380
36381     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
36382
36383     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
36384     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
36385     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
36386     var m, id = 0;
36387     var tpls = [];
36388
36389     while(m = s.match(re)){
36390        var m2 = m[0].match(nameRe);
36391        var m3 = m[0].match(ifRe);
36392        var m4 = m[0].match(execRe);
36393        var exp = null, fn = null, exec = null;
36394        var name = m2 && m2[1] ? m2[1] : '';
36395        if(m3){
36396            exp = m3 && m3[1] ? m3[1] : null;
36397            if(exp){
36398                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
36399            }
36400        }
36401        if(m4){
36402            exp = m4 && m4[1] ? m4[1] : null;
36403            if(exp){
36404                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
36405            }
36406        }
36407        if(name){
36408            switch(name){
36409                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
36410                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
36411                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
36412            }
36413        }
36414        tpls.push({
36415             id: id,
36416             target: name,
36417             exec: exec,
36418             test: fn,
36419             body: m[1]||''
36420         });
36421        s = s.replace(m[0], '{xtpl'+ id + '}');
36422        ++id;
36423     }
36424     for(var i = tpls.length-1; i >= 0; --i){
36425         this.compileTpl(tpls[i]);
36426     }
36427     this.master = tpls[tpls.length-1];
36428     this.tpls = tpls;
36429 };
36430 Roo.extend(Roo.XTemplate, Roo.Template, {
36431
36432     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
36433
36434     applySubTemplate : function(id, values, parent){
36435         var t = this.tpls[id];
36436         if(t.test && !t.test.call(this, values, parent)){
36437             return '';
36438         }
36439         if(t.exec && t.exec.call(this, values, parent)){
36440             return '';
36441         }
36442         var vs = t.target ? t.target.call(this, values, parent) : values;
36443         parent = t.target ? values : parent;
36444         if(t.target && vs instanceof Array){
36445             var buf = [];
36446             for(var i = 0, len = vs.length; i < len; i++){
36447                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
36448             }
36449             return buf.join('');
36450         }
36451         return t.compiled.call(this, vs, parent);
36452     },
36453
36454     compileTpl : function(tpl){
36455         var fm = Roo.util.Format;
36456         var useF = this.disableFormats !== true;
36457         var sep = Roo.isGecko ? "+" : ",";
36458         var fn = function(m, name, format, args){
36459             if(name.substr(0, 4) == 'xtpl'){
36460                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
36461             }
36462             var v;
36463             if(name.indexOf('.') != -1){
36464                 v = name;
36465             }else{
36466                 v = "values['" + name + "']";
36467             }
36468             if(format && useF){
36469                 args = args ? ',' + args : "";
36470                 if(format.substr(0, 5) != "this."){
36471                     format = "fm." + format + '(';
36472                 }else{
36473                     format = 'this.call("'+ format.substr(5) + '", ';
36474                     args = ", values";
36475                 }
36476             }else{
36477                 args= ''; format = "("+v+" === undefined ? '' : ";
36478             }
36479             return "'"+ sep + format + v + args + ")"+sep+"'";
36480         };
36481         var body;
36482         // branched to use + in gecko and [].join() in others
36483         if(Roo.isGecko){
36484             body = "tpl.compiled = function(values, parent){ return '" +
36485                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
36486                     "';};";
36487         }else{
36488             body = ["tpl.compiled = function(values, parent){ return ['"];
36489             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
36490             body.push("'].join('');};");
36491             body = body.join('');
36492         }
36493         /** eval:var:zzzzzzz */
36494         eval(body);
36495         return this;
36496     },
36497
36498     applyTemplate : function(values){
36499         return this.master.compiled.call(this, values, {});
36500         var s = this.subs;
36501     },
36502
36503     apply : function(){
36504         return this.applyTemplate.apply(this, arguments);
36505     },
36506
36507     compile : function(){return this;}
36508 });
36509
36510 Roo.XTemplate.from = function(el){
36511     el = Roo.getDom(el);
36512     return new Roo.XTemplate(el.value || el.innerHTML);
36513 };/*
36514  * Original code for Roojs - LGPL
36515  * <script type="text/javascript">
36516  */
36517  
36518 /**
36519  * @class Roo.XComponent
36520  * A delayed Element creator...
36521  * 
36522  * Mypart.xyx = new Roo.XComponent({
36523
36524     parent : 'Mypart.xyz', // empty == document.element.!!
36525     order : '001',
36526     name : 'xxxx'
36527     region : 'xxxx'
36528     disabled : function() {} 
36529      
36530     tree : function() { // return an tree of xtype declared components
36531         var MODULE = this;
36532         return 
36533         {
36534             xtype : 'NestedLayoutPanel',
36535             // technicall
36536         }
36537      ]
36538  *})
36539  * @extends Roo.util.Observable
36540  * @constructor
36541  * @param cfg {Object} configuration of component
36542  * 
36543  */
36544 Roo.XComponent = function(cfg) {
36545     Roo.apply(this, cfg);
36546     this.addEvents({ 
36547         /**
36548              * @event built
36549              * Fires when this the componnt is built
36550              * @param {Roo.XComponent} c the component
36551              */
36552         'built' : true,
36553         /**
36554              * @event buildcomplete
36555              * Fires on the top level element when all elements have been built
36556              * @param {Roo.XComponent} c the top level component.
36557          */
36558         'buildcomplete' : true
36559         
36560     });
36561     
36562     Roo.XComponent.register(this);
36563     this.modules = false;
36564     this.el = false; // where the layout goes..
36565     
36566     
36567 }
36568 Roo.extend(Roo.XComponent, Roo.util.Observable, {
36569     /**
36570      * @property el
36571      * The created element (with Roo.factory())
36572      * @type {Roo.Layout}
36573      */
36574     el  : false,
36575     
36576     /**
36577      * @property el
36578      * for BC  - use el in new code
36579      * @type {Roo.Layout}
36580      */
36581     panel : false,
36582     
36583     /**
36584      * @property layout
36585      * for BC  - use el in new code
36586      * @type {Roo.Layout}
36587      */
36588     layout : false,
36589     
36590      /**
36591      * @cfg {Function|boolean} disabled
36592      * If this module is disabled by some rule, return true from the funtion
36593      */
36594     disabled : false,
36595     
36596     /**
36597      * @cfg {String} parent 
36598      * Name of parent element which it get xtype added to..
36599      */
36600     parent: false,
36601     
36602     /**
36603      * @cfg {String} order
36604      * Used to set the order in which elements are created (usefull for multiple tabs)
36605      */
36606     
36607     order : false,
36608     /**
36609      * @cfg {String} name
36610      * String to display while loading.
36611      */
36612     name : false,
36613     /**
36614      * @cfg {Array} items
36615      * A single item array - the first element is the root of the tree..
36616      * It's done this way to stay compatible with the Xtype system...
36617      */
36618     items : false
36619      
36620      
36621     
36622 });
36623
36624 Roo.apply(Roo.XComponent, {
36625     
36626     /**
36627      * @property  buildCompleted
36628      * True when the builder has completed building the interface.
36629      * @type Boolean
36630      */
36631     buildCompleted : false,
36632      
36633     /**
36634      * @property  topModule
36635      * the upper most module - uses document.element as it's constructor.
36636      * @type Object
36637      */
36638      
36639     topModule  : false,
36640       
36641     /**
36642      * @property  modules
36643      * array of modules to be created by registration system.
36644      * @type Roo.XComponent
36645      */
36646     
36647     modules : [],
36648       
36649     
36650     /**
36651      * Register components to be built later.
36652      *
36653      * This solves the following issues
36654      * - Building is not done on page load, but after an authentication process has occured.
36655      * - Interface elements are registered on page load
36656      * - Parent Interface elements may not be loaded before child, so this handles that..
36657      * 
36658      *
36659      * example:
36660      * 
36661      * MyApp.register({
36662           order : '000001',
36663           module : 'Pman.Tab.projectMgr',
36664           region : 'center',
36665           parent : 'Pman.layout',
36666           disabled : false,  // or use a function..
36667         })
36668      
36669      * * @param {Object} details about module
36670      */
36671     register : function(obj) {
36672         this.modules.push(obj);
36673          
36674     },
36675     /**
36676      * convert a string to an object..
36677      * 
36678      */
36679     
36680     toObject : function(str)
36681     {
36682         if (!str || typeof(str) == 'object') {
36683             return str;
36684         }
36685         var ar = str.split('.');
36686         var rt, o;
36687         rt = ar.shift();
36688             /** eval:var:o */
36689         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
36690         if (o === false) {
36691             throw "Module not found : " + str;
36692         }
36693         Roo.each(ar, function(e) {
36694             if (typeof(o[e]) == 'undefined') {
36695                 throw "Module not found : " + str;
36696             }
36697             o = o[e];
36698         });
36699         return o;
36700         
36701     },
36702     
36703     
36704     /**
36705      * move modules into their correct place in the tree..
36706      * 
36707      */
36708     preBuild : function ()
36709     {
36710         
36711         Roo.each(this.modules , function (obj)
36712         {
36713             obj.parent = this.toObject(obj.parent);
36714             
36715             if (!obj.parent) {
36716                 this.topModule = obj;
36717                 return;
36718             }
36719             
36720             if (!obj.parent.modules) {
36721                 obj.parent.modules = new Roo.util.MixedCollection(false, 
36722                     function(o) { return o.order + '' }
36723                 );
36724             }
36725             
36726             obj.parent.modules.add(obj);
36727         }, this);
36728     },
36729     
36730      /**
36731      * make a list of modules to build.
36732      * @return {Array} list of modules. 
36733      */ 
36734     
36735     buildOrder : function()
36736     {
36737         var _this = this;
36738         var cmp = function(a,b) {   
36739             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
36740         };
36741         
36742         if (!this.topModule || !this.topModule.modules) {
36743             throw "No top level modules to build";
36744         }
36745        
36746         // make a flat list in order of modules to build.
36747         var mods = [ this.topModule ];
36748         
36749         
36750         // add modules to their parents..
36751         var addMod = function(m) {
36752            // Roo.debug && Roo.log(m.modKey);
36753             
36754             mods.push(m);
36755             if (m.modules) {
36756                 m.modules.keySort('ASC',  cmp );
36757                 m.modules.each(addMod);
36758             }
36759             // not sure if this is used any more..
36760             if (m.finalize) {
36761                 m.finalize.name = m.name + " (clean up) ";
36762                 mods.push(m.finalize);
36763             }
36764             
36765         }
36766         this.topModule.modules.keySort('ASC',  cmp );
36767         this.topModule.modules.each(addMod);
36768         return mods;
36769     },
36770     
36771      /**
36772      * Build the registered modules.
36773      * @param {Object} parent element.
36774      * @param {Function} optional method to call after module has been added.
36775      * 
36776      */ 
36777    
36778     build : function() 
36779     {
36780         
36781         this.preBuild();
36782         var mods = this.buildOrder();
36783       
36784         //this.allmods = mods;
36785         //Roo.debug && Roo.log(mods);
36786         //return;
36787         if (!mods.length) { // should not happen
36788             throw "NO modules!!!";
36789         }
36790         
36791         
36792         
36793         // flash it up as modal - so we store the mask!?
36794         Roo.MessageBox.show({ title: 'loading' });
36795         Roo.MessageBox.show({
36796            title: "Please wait...",
36797            msg: "Building Interface...",
36798            width:450,
36799            progress:true,
36800            closable:false,
36801            modal: false
36802           
36803         });
36804         var total = mods.length;
36805         
36806         var _this = this;
36807         var progressRun = function() {
36808             if (!mods.length) {
36809                 Roo.debug && Roo.log('hide?');
36810                 Roo.MessageBox.hide();
36811                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
36812                 return;    
36813             }
36814             
36815             var m = mods.shift();
36816             Roo.debug && Roo.log(m);
36817             if (typeof(m) == 'function') { // not sure if this is supported any more..
36818                 m.call(this);
36819                 return progressRun.defer(10, _this);
36820             } 
36821             
36822             Roo.MessageBox.updateProgress(
36823                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
36824                     " of " + total + 
36825                     (m.name ? (' - ' + m.name) : '')
36826                     );
36827             
36828          
36829             
36830             var disabled = (typeof(m.disabled) == 'function') ?
36831                 m.disabled.call(m.module.disabled) : m.disabled;    
36832             
36833             
36834             if (disabled) {
36835                 return progressRun(); // we do not update the display!
36836             }
36837             
36838             if (!m.parent) {
36839                 // it's a top level one..
36840                 var layoutbase = new Ext.BorderLayout(document.body, {
36841                
36842                     center: {
36843                          titlebar: false,
36844                          autoScroll:false,
36845                          closeOnTab: true,
36846                          tabPosition: 'top',
36847                          //resizeTabs: true,
36848                          alwaysShowTabs: true,
36849                          minTabWidth: 140
36850                     }
36851                 });
36852                 var tree = m.tree();
36853                 tree.region = 'center';
36854                 m.el = layoutbase.addxtype(tree);
36855                 m.panel = m.el;
36856                 m.layout = m.panel.layout;    
36857                 return progressRun.defer(10, _this);
36858             }
36859             
36860             var tree = m.tree();
36861             tree.region = tree.region || m.region;
36862             m.el = m.parent.el.addxtype(tree);
36863             m.fireEvent('built', m);
36864             m.panel = m.el;
36865             m.layout = m.panel.layout;    
36866             progressRun.defer(10, _this); 
36867             
36868         }
36869         progressRun.defer(1, _this);
36870      
36871         
36872         
36873     }
36874      
36875    
36876     
36877     
36878 });
36879  //<script type="text/javascript">
36880
36881
36882 /**
36883  * @class Roo.Login
36884  * @extends Roo.LayoutDialog
36885  * A generic Login Dialog..... - only one needed in theory!?!?
36886  *
36887  * Fires XComponent builder on success...
36888  * 
36889  * Sends 
36890  *    username,password, lang = for login actions.
36891  *    check = 1 for periodic checking that sesion is valid.
36892  *    passwordRequest = email request password
36893  *    logout = 1 = to logout
36894  * 
36895  * Affects: (this id="????" elements)
36896  *   loading  (removed) (used to indicate application is loading)
36897  *   loading-mask (hides) (used to hide application when it's building loading)
36898  *   
36899  * 
36900  * Usage: 
36901  *    
36902  * 
36903  * Myapp.login = Roo.Login({
36904      url: xxxx,
36905    
36906      realm : 'Myapp', 
36907      
36908      
36909      method : 'POST',
36910      
36911      
36912      * 
36913  })
36914  * 
36915  * 
36916  * 
36917  **/
36918  
36919 Roo.Login = function(cfg)
36920 {
36921     this.addEvents({
36922         'refreshed' : true
36923     });
36924     
36925     Roo.apply(this,cfg);
36926     
36927     Roo.onReady(function() {
36928         this.onLoad();
36929     }, this);
36930     // call parent..
36931     
36932    
36933     Roo.Login.superclass.constructor.call(this, this);
36934     //this.addxtype(this.items[0]);
36935     
36936     
36937 }
36938
36939
36940 Roo.extend(Roo.Login, Roo.LayoutDialog, {
36941     
36942     /**
36943      * @cfg {String} method
36944      * Method used to query for login details.
36945      */
36946     
36947     method : 'POST',
36948     /**
36949      * @cfg {String} url
36950      * URL to query login data. - eg. baseURL + '/Login.php'
36951      */
36952     url : '',
36953     
36954     /**
36955      * @property user
36956      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
36957      * @type {Object} 
36958      */
36959     user : false,
36960     /**
36961      * @property checkFails
36962      * Number of times we have attempted to get authentication check, and failed.
36963      * @type {Number} 
36964      */
36965     checkFails : 0,
36966       /**
36967      * @property intervalID
36968      * The window interval that does the constant login checking.
36969      * @type {Number} 
36970      */
36971     intervalID : 0,
36972     
36973     
36974     onLoad : function() // called on page load...
36975     {
36976         // load 
36977          
36978         if (Roo.get('loading')) { // clear any loading indicator..
36979             Roo.get('loading').remove();
36980         }
36981         
36982         //this.switchLang('en'); // set the language to english..
36983        
36984         this.check({
36985             success:  function(response, opts)  {  // check successfull...
36986             
36987                 var res = this.processResponse(response);
36988                 this.checkFails =0;
36989                 if (!res.success) { // error!
36990                     this.checkFails = 5;
36991                     //console.log('call failure');
36992                     return this.failure(response,opts);
36993                 }
36994                 
36995                 if (!res.data.id) { // id=0 == login failure.
36996                     return this.show();
36997                 }
36998                 
36999                               
37000                         //console.log(success);
37001                 this.fillAuth(res.data);   
37002                 this.checkFails =0;
37003                 Roo.XComponent.build();
37004             },
37005             failure : this.show
37006         });
37007         
37008     }, 
37009     
37010     
37011     check: function(cfg) // called every so often to refresh cookie etc..
37012     {
37013         if (cfg.again) { // could be undefined..
37014             this.checkFails++;
37015         } else {
37016             this.checkFails = 0;
37017         }
37018         var _this = this;
37019         if (this.sending) {
37020             if ( this.checkFails > 4) {
37021                 Roo.MessageBox.alert("Error",  
37022                     "Error getting authentication status. - try reloading, or wait a while", function() {
37023                         _this.sending = false;
37024                     }); 
37025                 return;
37026             }
37027             cfg.again = true;
37028             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
37029             return;
37030         }
37031         this.sending = true;
37032         
37033         Roo.Ajax.request({  
37034             url: this.url,
37035             params: {
37036                 getAuthUser: true
37037             },  
37038             method: this.method,
37039             success:  cfg.success || this.success,
37040             failure : cfg.failure || this.failure,
37041             scope : this,
37042             callCfg : cfg
37043               
37044         });  
37045     }, 
37046     
37047     
37048     logout: function()
37049     {
37050         window.onbeforeunload = function() { }; // false does not work for IE..
37051         this.user = false;
37052         var _this = this;
37053         
37054         Roo.Ajax.request({  
37055             url: this.url,
37056             params: {
37057                 logout: 1
37058             },  
37059             method: 'GET',
37060             failure : function() {
37061                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
37062                     document.location = document.location.toString() + '?ts=' + Math.random();
37063                 });
37064                 
37065             },
37066             success : function() {
37067                 _this.user = false;
37068                 this.checkFails =0;
37069                 // fixme..
37070                 document.location = document.location.toString() + '?ts=' + Math.random();
37071             }
37072               
37073               
37074         }); 
37075     },
37076     
37077     processResponse : function (response)
37078     {
37079         var res = '';
37080         try {
37081             res = Roo.decode(response.responseText);
37082             // oops...
37083             if (typeof(res) != 'object') {
37084                 res = { success : false, errorMsg : res, errors : true };
37085             }
37086             if (typeof(res.success) == 'undefined') {
37087                 res.success = false;
37088             }
37089             
37090         } catch(e) {
37091             res = { success : false,  errorMsg : response.responseText, errors : true };
37092         }
37093         return res;
37094     },
37095     
37096     success : function(response, opts)  // check successfull...
37097     {  
37098         this.sending = false;
37099         var res = this.processResponse(response);
37100         if (!res.success) {
37101             return this.failure(response, opts);
37102         }
37103         if (!res.data || !res.data.id) {
37104             return this.failure(response,opts);
37105         }
37106         //console.log(res);
37107         this.fillAuth(res.data);
37108         
37109         this.checkFails =0;
37110         
37111     },
37112     
37113     
37114     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
37115     {
37116         this.authUser = -1;
37117         this.sending = false;
37118         var res = this.processResponse(response);
37119         //console.log(res);
37120         if ( this.checkFails > 2) {
37121         
37122             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
37123                 "Error getting authentication status. - try reloading"); 
37124             return;
37125         }
37126         opts.callCfg.again = true;
37127         this.check.defer(1000, this, [ opts.callCfg ]);
37128         return;  
37129     },
37130     
37131     
37132     
37133     fillAuth: function(au) {
37134         this.startAuthCheck();
37135         this.authUserId = au.id;
37136         this.authUser = au;
37137         this.lastChecked = new Date();
37138         this.fireEvent('refreshed', au);
37139         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
37140         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
37141         au.lang = au.lang || 'en';
37142         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
37143         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
37144         this.switchLang(au.lang );
37145         
37146      
37147         // open system... - -on setyp..
37148         if (this.authUserId  < 0) {
37149             Roo.MessageBox.alert("Warning", 
37150                 "This is an open system - please set up a admin user with a password.");  
37151         }
37152          
37153         //Pman.onload(); // which should do nothing if it's a re-auth result...
37154         
37155              
37156     },
37157     
37158     startAuthCheck : function() // starter for timeout checking..
37159     {
37160         if (this.intervalID) { // timer already in place...
37161             return false;
37162         }
37163         var _this = this;
37164         this.intervalID =  window.setInterval(function() {
37165               _this.check(false);
37166             }, 120000); // every 120 secs = 2mins..
37167         
37168         
37169     },
37170          
37171     
37172     switchLang : function (lang) 
37173     {
37174         _T = typeof(_T) == 'undefined' ? false : _T;
37175           if (!_T || !lang.length) {
37176             return;
37177         }
37178         
37179         if (!_T && lang != 'en') {
37180             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37181             return;
37182         }
37183         
37184         if (typeof(_T.en) == 'undefined') {
37185             _T.en = {};
37186             Roo.apply(_T.en, _T);
37187         }
37188         
37189         if (typeof(_T[lang]) == 'undefined') {
37190             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37191             return;
37192         }
37193         
37194         
37195         Roo.apply(_T, _T[lang]);
37196         // just need to set the text values for everything...
37197         var _this = this;
37198         /* this will not work ...
37199         if (this.form) { 
37200             
37201                
37202             function formLabel(name, val) {
37203                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
37204             }
37205             
37206             formLabel('password', "Password"+':');
37207             formLabel('username', "Email Address"+':');
37208             formLabel('lang', "Language"+':');
37209             this.dialog.setTitle("Login");
37210             this.dialog.buttons[0].setText("Forgot Password");
37211             this.dialog.buttons[1].setText("Login");
37212         }
37213         */
37214         
37215         
37216     },
37217     
37218     
37219     title: "Login",
37220     modal: true,
37221     width:  350,
37222     //height: 230,
37223     height: 180,
37224     shadow: true,
37225     minWidth:200,
37226     minHeight:180,
37227     //proxyDrag: true,
37228     closable: false,
37229     draggable: false,
37230     collapsible: false,
37231     resizable: false,
37232     center: {  // needed??
37233         autoScroll:false,
37234         titlebar: false,
37235        // tabPosition: 'top',
37236         hideTabs: true,
37237         closeOnTab: true,
37238         alwaysShowTabs: false
37239     } ,
37240     listeners : {
37241         
37242         show  : function(dlg)
37243         {
37244             //console.log(this);
37245             this.form = this.layout.getRegion('center').activePanel.form;
37246             this.form.dialog = dlg;
37247             this.buttons[0].form = this.form;
37248             this.buttons[0].dialog = dlg;
37249             this.buttons[1].form = this.form;
37250             this.buttons[1].dialog = dlg;
37251            
37252            //this.resizeToLogo.defer(1000,this);
37253             // this is all related to resizing for logos..
37254             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
37255            //// if (!sz) {
37256              //   this.resizeToLogo.defer(1000,this);
37257              //   return;
37258            // }
37259             //var w = Ext.lib.Dom.getViewWidth() - 100;
37260             //var h = Ext.lib.Dom.getViewHeight() - 100;
37261             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
37262             //this.center();
37263             if (this.disabled) {
37264                 this.hide();
37265                 return;
37266             }
37267             
37268             if (this.user.id < 0) { // used for inital setup situations.
37269                 return;
37270             }
37271             
37272             if (this.intervalID) {
37273                 // remove the timer
37274                 window.clearInterval(this.intervalID);
37275                 this.intervalID = false;
37276             }
37277             
37278             
37279             if (Roo.get('loading')) {
37280                 Roo.get('loading').remove();
37281             }
37282             if (Roo.get('loading-mask')) {
37283                 Roo.get('loading-mask').hide();
37284             }
37285             
37286             //incomming._node = tnode;
37287             this.form.reset();
37288             //this.dialog.modal = !modal;
37289             //this.dialog.show();
37290             this.el.unmask(); 
37291             
37292             
37293             this.form.setValues({
37294                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
37295                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
37296             });
37297             
37298             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
37299             if (this.form.findField('username').getValue().length > 0 ){
37300                 this.form.findField('password').focus();
37301             } else {
37302                this.form.findField('username').focus();
37303             }
37304     
37305         }
37306     },
37307     items : [
37308          {
37309        
37310             xtype : 'ContentPanel',
37311             xns : Roo,
37312             region: 'center',
37313             fitToFrame : true,
37314             
37315             items : [
37316     
37317                 {
37318                
37319                     xtype : 'Form',
37320                     xns : Roo.form,
37321                     labelWidth: 100,
37322                     style : 'margin: 10px;',
37323                     
37324                     listeners : {
37325                         actionfailed : function(f, act) {
37326                             // form can return { errors: .... }
37327                                 
37328                             //act.result.errors // invalid form element list...
37329                             //act.result.errorMsg// invalid form element list...
37330                             
37331                             this.dialog.el.unmask();
37332                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
37333                                         "Login failed - communication error - try again.");
37334                                       
37335                         },
37336                         actioncomplete: function(re, act) {
37337                              
37338                             Roo.state.Manager.set(
37339                                 this.dialog.realm + '.username',  
37340                                     this.findField('username').getValue()
37341                             );
37342                             Roo.state.Manager.set(
37343                                 this.dialog.realm + '.lang',  
37344                                 this.findField('lang').getValue() 
37345                             );
37346                             
37347                             this.dialog.fillAuth(act.result.data);
37348                               
37349                             this.dialog.hide();
37350                             
37351                             if (Roo.get('loading-mask')) {
37352                                 Roo.get('loading-mask').show();
37353                             }
37354                             Roo.XComponent.build();
37355                             
37356                              
37357                             
37358                         }
37359                     },
37360                     items : [
37361                         {
37362                             xtype : 'TextField',
37363                             xns : Roo.form,
37364                             fieldLabel: "Email Address",
37365                             name: 'username',
37366                             width:200,
37367                             autoCreate : {tag: "input", type: "text", size: "20"}
37368                         },
37369                         {
37370                             xtype : 'TextField',
37371                             xns : Roo.form,
37372                             fieldLabel: "Password",
37373                             inputType: 'password',
37374                             name: 'password',
37375                             width:200,
37376                             autoCreate : {tag: "input", type: "text", size: "20"},
37377                             listeners : {
37378                                 specialkey : function(e,ev) {
37379                                     if (ev.keyCode == 13) {
37380                                         this.form.dialog.el.mask("Logging in");
37381                                         this.form.doAction('submit', {
37382                                             url: this.form.dialog.url,
37383                                             method: this.form.dialog.method
37384                                         });
37385                                     }
37386                                 }
37387                             }  
37388                         },
37389                         {
37390                             xtype : 'ComboBox',
37391                             xns : Roo.form,
37392                             fieldLabel: "Language",
37393                             name : 'langdisp',
37394                             store: {
37395                                 xtype : 'SimpleStore',
37396                                 fields: ['lang', 'ldisp'],
37397                                 data : [
37398                                     [ 'en', 'English' ],
37399                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
37400                                     [ 'zh_CN', '\u7C21\u4E2D' ]
37401                                 ]
37402                             },
37403                             
37404                             valueField : 'lang',
37405                             hiddenName:  'lang',
37406                             width: 200,
37407                             displayField:'ldisp',
37408                             typeAhead: false,
37409                             editable: false,
37410                             mode: 'local',
37411                             triggerAction: 'all',
37412                             emptyText:'Select a Language...',
37413                             selectOnFocus:true,
37414                             listeners : {
37415                                 select :  function(cb, rec, ix) {
37416                                     this.form.switchLang(rec.data.lang);
37417                                 }
37418                             }
37419                         
37420                         }
37421                     ]
37422                 }
37423                   
37424                 
37425             ]
37426         }
37427     ],
37428     buttons : [
37429         {
37430             xtype : 'Button',
37431             xns : 'Roo',
37432             text : "Forgot Password",
37433             listeners : {
37434                 click : function() {
37435                     //console.log(this);
37436                     var n = this.form.findField('username').getValue();
37437                     if (!n.length) {
37438                         Roo.MessageBox.alert("Error", "Fill in your email address");
37439                         return;
37440                     }
37441                     Roo.Ajax.request({
37442                         url: this.dialog.url,
37443                         params: {
37444                             passwordRequest: n
37445                         },
37446                         method: this.dialog.method,
37447                         success:  function(response, opts)  {  // check successfull...
37448                         
37449                             var res = this.dialog.processResponse(response);
37450                             if (!res.success) { // error!
37451                                Roo.MessageBox.alert("Error" ,
37452                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
37453                                return;
37454                             }
37455                             Roo.MessageBox.alert("Notice" ,
37456                                 "Please check you email for the Password Reset message");
37457                         },
37458                         failure : function() {
37459                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
37460                         }
37461                         
37462                     });
37463                 }
37464             }
37465         },
37466         {
37467             xtype : 'Button',
37468             xns : 'Roo',
37469             text : "Login",
37470             listeners : {
37471                 
37472                 click : function () {
37473                         
37474                     this.dialog.el.mask("Logging in");
37475                     this.form.doAction('submit', {
37476                             url: this.dialog.url,
37477                             method: this.dialog.method
37478                     });
37479                 }
37480             }
37481         }
37482     ]
37483   
37484   
37485 })
37486  
37487
37488
37489