roojs-ui-debug.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     
3044     
3045     var onStop = function(e){
3046         dragEl = null;
3047         clearProc();
3048     };
3049     
3050     var triggerRefresh = function(){
3051         if(ddm.dragCurrent){
3052              ddm.refreshCache(ddm.dragCurrent.groups);
3053         }
3054     };
3055     
3056     var doScroll = function(){
3057         if(ddm.dragCurrent){
3058             var dds = Roo.dd.ScrollManager;
3059             if(!dds.animate){
3060                 if(proc.el.scroll(proc.dir, dds.increment)){
3061                     triggerRefresh();
3062                 }
3063             }else{
3064                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3065             }
3066         }
3067     };
3068     
3069     var clearProc = function(){
3070         if(proc.id){
3071             clearInterval(proc.id);
3072         }
3073         proc.id = 0;
3074         proc.el = null;
3075         proc.dir = "";
3076     };
3077     
3078     var startProc = function(el, dir){
3079          Roo.log('scroll startproc');
3080         clearProc();
3081         proc.el = el;
3082         proc.dir = dir;
3083         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3084     };
3085     
3086     var onFire = function(e, isDrop){
3087        
3088         if(isDrop || !ddm.dragCurrent){ return; }
3089         var dds = Roo.dd.ScrollManager;
3090         if(!dragEl || dragEl != ddm.dragCurrent){
3091             dragEl = ddm.dragCurrent;
3092             // refresh regions on drag start
3093             dds.refreshCache();
3094         }
3095         
3096         var xy = Roo.lib.Event.getXY(e);
3097         var pt = new Roo.lib.Point(xy[0], xy[1]);
3098         for(var id in els){
3099             var el = els[id], r = el._region;
3100             if(r && r.contains(pt) && el.isScrollable()){
3101                 if(r.bottom - pt.y <= dds.thresh){
3102                     if(proc.el != el){
3103                         startProc(el, "down");
3104                     }
3105                     return;
3106                 }else if(r.right - pt.x <= dds.thresh){
3107                     if(proc.el != el){
3108                         startProc(el, "left");
3109                     }
3110                     return;
3111                 }else if(pt.y - r.top <= dds.thresh){
3112                     if(proc.el != el){
3113                         startProc(el, "up");
3114                     }
3115                     return;
3116                 }else if(pt.x - r.left <= dds.thresh){
3117                     if(proc.el != el){
3118                         startProc(el, "right");
3119                     }
3120                     return;
3121                 }
3122             }
3123         }
3124         clearProc();
3125     };
3126     
3127     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3128     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3129     
3130     return {
3131         /**
3132          * Registers new overflow element(s) to auto scroll
3133          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3134          */
3135         register : function(el){
3136             if(el instanceof Array){
3137                 for(var i = 0, len = el.length; i < len; i++) {
3138                         this.register(el[i]);
3139                 }
3140             }else{
3141                 el = Roo.get(el);
3142                 els[el.id] = el;
3143             }
3144             Roo.dd.ScrollManager.els = els;
3145         },
3146         
3147         /**
3148          * Unregisters overflow element(s) so they are no longer scrolled
3149          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3150          */
3151         unregister : function(el){
3152             if(el instanceof Array){
3153                 for(var i = 0, len = el.length; i < len; i++) {
3154                         this.unregister(el[i]);
3155                 }
3156             }else{
3157                 el = Roo.get(el);
3158                 delete els[el.id];
3159             }
3160         },
3161         
3162         /**
3163          * The number of pixels from the edge of a container the pointer needs to be to 
3164          * trigger scrolling (defaults to 25)
3165          * @type Number
3166          */
3167         thresh : 25,
3168         
3169         /**
3170          * The number of pixels to scroll in each scroll increment (defaults to 50)
3171          * @type Number
3172          */
3173         increment : 100,
3174         
3175         /**
3176          * The frequency of scrolls in milliseconds (defaults to 500)
3177          * @type Number
3178          */
3179         frequency : 500,
3180         
3181         /**
3182          * True to animate the scroll (defaults to true)
3183          * @type Boolean
3184          */
3185         animate: true,
3186         
3187         /**
3188          * The animation duration in seconds - 
3189          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3190          * @type Number
3191          */
3192         animDuration: .4,
3193         
3194         /**
3195          * Manually trigger a cache refresh.
3196          */
3197         refreshCache : function(){
3198             for(var id in els){
3199                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3200                     els[id]._region = els[id].getRegion();
3201                 }
3202             }
3203         }
3204     };
3205 }();/*
3206  * Based on:
3207  * Ext JS Library 1.1.1
3208  * Copyright(c) 2006-2007, Ext JS, LLC.
3209  *
3210  * Originally Released Under LGPL - original licence link has changed is not relivant.
3211  *
3212  * Fork - LGPL
3213  * <script type="text/javascript">
3214  */
3215  
3216
3217 /**
3218  * @class Roo.dd.Registry
3219  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3220  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3221  * @singleton
3222  */
3223 Roo.dd.Registry = function(){
3224     var elements = {}; 
3225     var handles = {}; 
3226     var autoIdSeed = 0;
3227
3228     var getId = function(el, autogen){
3229         if(typeof el == "string"){
3230             return el;
3231         }
3232         var id = el.id;
3233         if(!id && autogen !== false){
3234             id = "roodd-" + (++autoIdSeed);
3235             el.id = id;
3236         }
3237         return id;
3238     };
3239     
3240     return {
3241     /**
3242      * Register a drag drop element
3243      * @param {String|HTMLElement} element The id or DOM node to register
3244      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3245      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3246      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3247      * populated in the data object (if applicable):
3248      * <pre>
3249 Value      Description<br />
3250 ---------  ------------------------------------------<br />
3251 handles    Array of DOM nodes that trigger dragging<br />
3252            for the element being registered<br />
3253 isHandle   True if the element passed in triggers<br />
3254            dragging itself, else false
3255 </pre>
3256      */
3257         register : function(el, data){
3258             data = data || {};
3259             if(typeof el == "string"){
3260                 el = document.getElementById(el);
3261             }
3262             data.ddel = el;
3263             elements[getId(el)] = data;
3264             if(data.isHandle !== false){
3265                 handles[data.ddel.id] = data;
3266             }
3267             if(data.handles){
3268                 var hs = data.handles;
3269                 for(var i = 0, len = hs.length; i < len; i++){
3270                         handles[getId(hs[i])] = data;
3271                 }
3272             }
3273         },
3274
3275     /**
3276      * Unregister a drag drop element
3277      * @param {String|HTMLElement}  element The id or DOM node to unregister
3278      */
3279         unregister : function(el){
3280             var id = getId(el, false);
3281             var data = elements[id];
3282             if(data){
3283                 delete elements[id];
3284                 if(data.handles){
3285                     var hs = data.handles;
3286                     for(var i = 0, len = hs.length; i < len; i++){
3287                         delete handles[getId(hs[i], false)];
3288                     }
3289                 }
3290             }
3291         },
3292
3293     /**
3294      * Returns the handle registered for a DOM Node by id
3295      * @param {String|HTMLElement} id The DOM node or id to look up
3296      * @return {Object} handle The custom handle data
3297      */
3298         getHandle : function(id){
3299             if(typeof id != "string"){ // must be element?
3300                 id = id.id;
3301             }
3302             return handles[id];
3303         },
3304
3305     /**
3306      * Returns the handle that is registered for the DOM node that is the target of the event
3307      * @param {Event} e The event
3308      * @return {Object} handle The custom handle data
3309      */
3310         getHandleFromEvent : function(e){
3311             var t = Roo.lib.Event.getTarget(e);
3312             return t ? handles[t.id] : null;
3313         },
3314
3315     /**
3316      * Returns a custom data object that is registered for a DOM node by id
3317      * @param {String|HTMLElement} id The DOM node or id to look up
3318      * @return {Object} data The custom data
3319      */
3320         getTarget : function(id){
3321             if(typeof id != "string"){ // must be element?
3322                 id = id.id;
3323             }
3324             return elements[id];
3325         },
3326
3327     /**
3328      * Returns a custom data object that is registered for the DOM node that is the target of the event
3329      * @param {Event} e The event
3330      * @return {Object} data The custom data
3331      */
3332         getTargetFromEvent : function(e){
3333             var t = Roo.lib.Event.getTarget(e);
3334             return t ? elements[t.id] || handles[t.id] : null;
3335         }
3336     };
3337 }();/*
3338  * Based on:
3339  * Ext JS Library 1.1.1
3340  * Copyright(c) 2006-2007, Ext JS, LLC.
3341  *
3342  * Originally Released Under LGPL - original licence link has changed is not relivant.
3343  *
3344  * Fork - LGPL
3345  * <script type="text/javascript">
3346  */
3347  
3348
3349 /**
3350  * @class Roo.dd.StatusProxy
3351  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3352  * default drag proxy used by all Roo.dd components.
3353  * @constructor
3354  * @param {Object} config
3355  */
3356 Roo.dd.StatusProxy = function(config){
3357     Roo.apply(this, config);
3358     this.id = this.id || Roo.id();
3359     this.el = new Roo.Layer({
3360         dh: {
3361             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3362                 {tag: "div", cls: "x-dd-drop-icon"},
3363                 {tag: "div", cls: "x-dd-drag-ghost"}
3364             ]
3365         }, 
3366         shadow: !config || config.shadow !== false
3367     });
3368     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3369     this.dropStatus = this.dropNotAllowed;
3370 };
3371
3372 Roo.dd.StatusProxy.prototype = {
3373     /**
3374      * @cfg {String} dropAllowed
3375      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3376      */
3377     dropAllowed : "x-dd-drop-ok",
3378     /**
3379      * @cfg {String} dropNotAllowed
3380      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3381      */
3382     dropNotAllowed : "x-dd-drop-nodrop",
3383
3384     /**
3385      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3386      * over the current target element.
3387      * @param {String} cssClass The css class for the new drop status indicator image
3388      */
3389     setStatus : function(cssClass){
3390         cssClass = cssClass || this.dropNotAllowed;
3391         if(this.dropStatus != cssClass){
3392             this.el.replaceClass(this.dropStatus, cssClass);
3393             this.dropStatus = cssClass;
3394         }
3395     },
3396
3397     /**
3398      * Resets the status indicator to the default dropNotAllowed value
3399      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3400      */
3401     reset : function(clearGhost){
3402         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3403         this.dropStatus = this.dropNotAllowed;
3404         if(clearGhost){
3405             this.ghost.update("");
3406         }
3407     },
3408
3409     /**
3410      * Updates the contents of the ghost element
3411      * @param {String} html The html that will replace the current innerHTML of the ghost element
3412      */
3413     update : function(html){
3414         if(typeof html == "string"){
3415             this.ghost.update(html);
3416         }else{
3417             this.ghost.update("");
3418             html.style.margin = "0";
3419             this.ghost.dom.appendChild(html);
3420         }
3421         // ensure float = none set?? cant remember why though.
3422         var el = this.ghost.dom.firstChild;
3423                 if(el){
3424                         Roo.fly(el).setStyle('float', 'none');
3425                 }
3426     },
3427     
3428     /**
3429      * Returns the underlying proxy {@link Roo.Layer}
3430      * @return {Roo.Layer} el
3431     */
3432     getEl : function(){
3433         return this.el;
3434     },
3435
3436     /**
3437      * Returns the ghost element
3438      * @return {Roo.Element} el
3439      */
3440     getGhost : function(){
3441         return this.ghost;
3442     },
3443
3444     /**
3445      * Hides the proxy
3446      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3447      */
3448     hide : function(clear){
3449         this.el.hide();
3450         if(clear){
3451             this.reset(true);
3452         }
3453     },
3454
3455     /**
3456      * Stops the repair animation if it's currently running
3457      */
3458     stop : function(){
3459         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3460             this.anim.stop();
3461         }
3462     },
3463
3464     /**
3465      * Displays this proxy
3466      */
3467     show : function(){
3468         this.el.show();
3469     },
3470
3471     /**
3472      * Force the Layer to sync its shadow and shim positions to the element
3473      */
3474     sync : function(){
3475         this.el.sync();
3476     },
3477
3478     /**
3479      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3480      * invalid drop operation by the item being dragged.
3481      * @param {Array} xy The XY position of the element ([x, y])
3482      * @param {Function} callback The function to call after the repair is complete
3483      * @param {Object} scope The scope in which to execute the callback
3484      */
3485     repair : function(xy, callback, scope){
3486         this.callback = callback;
3487         this.scope = scope;
3488         if(xy && this.animRepair !== false){
3489             this.el.addClass("x-dd-drag-repair");
3490             this.el.hideUnders(true);
3491             this.anim = this.el.shift({
3492                 duration: this.repairDuration || .5,
3493                 easing: 'easeOut',
3494                 xy: xy,
3495                 stopFx: true,
3496                 callback: this.afterRepair,
3497                 scope: this
3498             });
3499         }else{
3500             this.afterRepair();
3501         }
3502     },
3503
3504     // private
3505     afterRepair : function(){
3506         this.hide(true);
3507         if(typeof this.callback == "function"){
3508             this.callback.call(this.scope || this);
3509         }
3510         this.callback = null;
3511         this.scope = null;
3512     }
3513 };/*
3514  * Based on:
3515  * Ext JS Library 1.1.1
3516  * Copyright(c) 2006-2007, Ext JS, LLC.
3517  *
3518  * Originally Released Under LGPL - original licence link has changed is not relivant.
3519  *
3520  * Fork - LGPL
3521  * <script type="text/javascript">
3522  */
3523
3524 /**
3525  * @class Roo.dd.DragSource
3526  * @extends Roo.dd.DDProxy
3527  * A simple class that provides the basic implementation needed to make any element draggable.
3528  * @constructor
3529  * @param {String/HTMLElement/Element} el The container element
3530  * @param {Object} config
3531  */
3532 Roo.dd.DragSource = function(el, config){
3533     this.el = Roo.get(el);
3534     this.dragData = {};
3535     
3536     Roo.apply(this, config);
3537     
3538     if(!this.proxy){
3539         this.proxy = new Roo.dd.StatusProxy();
3540     }
3541
3542     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3543           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3544     
3545     this.dragging = false;
3546 };
3547
3548 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3549     /**
3550      * @cfg {String} dropAllowed
3551      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3552      */
3553     dropAllowed : "x-dd-drop-ok",
3554     /**
3555      * @cfg {String} dropNotAllowed
3556      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3557      */
3558     dropNotAllowed : "x-dd-drop-nodrop",
3559
3560     /**
3561      * Returns the data object associated with this drag source
3562      * @return {Object} data An object containing arbitrary data
3563      */
3564     getDragData : function(e){
3565         return this.dragData;
3566     },
3567
3568     // private
3569     onDragEnter : function(e, id){
3570         var target = Roo.dd.DragDropMgr.getDDById(id);
3571         this.cachedTarget = target;
3572         if(this.beforeDragEnter(target, e, id) !== false){
3573             if(target.isNotifyTarget){
3574                 var status = target.notifyEnter(this, e, this.dragData);
3575                 this.proxy.setStatus(status);
3576             }else{
3577                 this.proxy.setStatus(this.dropAllowed);
3578             }
3579             
3580             if(this.afterDragEnter){
3581                 /**
3582                  * An empty function by default, but provided so that you can perform a custom action
3583                  * when the dragged item enters the drop target by providing an implementation.
3584                  * @param {Roo.dd.DragDrop} target The drop target
3585                  * @param {Event} e The event object
3586                  * @param {String} id The id of the dragged element
3587                  * @method afterDragEnter
3588                  */
3589                 this.afterDragEnter(target, e, id);
3590             }
3591         }
3592     },
3593
3594     /**
3595      * An empty function by default, but provided so that you can perform a custom action
3596      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3597      * @param {Roo.dd.DragDrop} target The drop target
3598      * @param {Event} e The event object
3599      * @param {String} id The id of the dragged element
3600      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3601      */
3602     beforeDragEnter : function(target, e, id){
3603         return true;
3604     },
3605
3606     // private
3607     alignElWithMouse: function() {
3608         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3609         this.proxy.sync();
3610     },
3611
3612     // private
3613     onDragOver : function(e, id){
3614         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3615         if(this.beforeDragOver(target, e, id) !== false){
3616             if(target.isNotifyTarget){
3617                 var status = target.notifyOver(this, e, this.dragData);
3618                 this.proxy.setStatus(status);
3619             }
3620
3621             if(this.afterDragOver){
3622                 /**
3623                  * An empty function by default, but provided so that you can perform a custom action
3624                  * while the dragged item is over the drop target by providing an implementation.
3625                  * @param {Roo.dd.DragDrop} target The drop target
3626                  * @param {Event} e The event object
3627                  * @param {String} id The id of the dragged element
3628                  * @method afterDragOver
3629                  */
3630                 this.afterDragOver(target, e, id);
3631             }
3632         }
3633     },
3634
3635     /**
3636      * An empty function by default, but provided so that you can perform a custom action
3637      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3638      * @param {Roo.dd.DragDrop} target The drop target
3639      * @param {Event} e The event object
3640      * @param {String} id The id of the dragged element
3641      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3642      */
3643     beforeDragOver : function(target, e, id){
3644         return true;
3645     },
3646
3647     // private
3648     onDragOut : function(e, id){
3649         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3650         if(this.beforeDragOut(target, e, id) !== false){
3651             if(target.isNotifyTarget){
3652                 target.notifyOut(this, e, this.dragData);
3653             }
3654             this.proxy.reset();
3655             if(this.afterDragOut){
3656                 /**
3657                  * An empty function by default, but provided so that you can perform a custom action
3658                  * after the dragged item is dragged out of the target without dropping.
3659                  * @param {Roo.dd.DragDrop} target The drop target
3660                  * @param {Event} e The event object
3661                  * @param {String} id The id of the dragged element
3662                  * @method afterDragOut
3663                  */
3664                 this.afterDragOut(target, e, id);
3665             }
3666         }
3667         this.cachedTarget = null;
3668     },
3669
3670     /**
3671      * An empty function by default, but provided so that you can perform a custom action before the dragged
3672      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3673      * @param {Roo.dd.DragDrop} target The drop target
3674      * @param {Event} e The event object
3675      * @param {String} id The id of the dragged element
3676      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3677      */
3678     beforeDragOut : function(target, e, id){
3679         return true;
3680     },
3681     
3682     // private
3683     onDragDrop : function(e, id){
3684         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3685         if(this.beforeDragDrop(target, e, id) !== false){
3686             if(target.isNotifyTarget){
3687                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3688                     this.onValidDrop(target, e, id);
3689                 }else{
3690                     this.onInvalidDrop(target, e, id);
3691                 }
3692             }else{
3693                 this.onValidDrop(target, e, id);
3694             }
3695             
3696             if(this.afterDragDrop){
3697                 /**
3698                  * An empty function by default, but provided so that you can perform a custom action
3699                  * after a valid drag drop has occurred by providing an implementation.
3700                  * @param {Roo.dd.DragDrop} target The drop target
3701                  * @param {Event} e The event object
3702                  * @param {String} id The id of the dropped element
3703                  * @method afterDragDrop
3704                  */
3705                 this.afterDragDrop(target, e, id);
3706             }
3707         }
3708         delete this.cachedTarget;
3709     },
3710
3711     /**
3712      * An empty function by default, but provided so that you can perform a custom action before the dragged
3713      * item is dropped onto the target and optionally cancel the onDragDrop.
3714      * @param {Roo.dd.DragDrop} target The drop target
3715      * @param {Event} e The event object
3716      * @param {String} id The id of the dragged element
3717      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3718      */
3719     beforeDragDrop : function(target, e, id){
3720         return true;
3721     },
3722
3723     // private
3724     onValidDrop : function(target, e, id){
3725         this.hideProxy();
3726         if(this.afterValidDrop){
3727             /**
3728              * An empty function by default, but provided so that you can perform a custom action
3729              * after a valid drop has occurred by providing an implementation.
3730              * @param {Object} target The target DD 
3731              * @param {Event} e The event object
3732              * @param {String} id The id of the dropped element
3733              * @method afterInvalidDrop
3734              */
3735             this.afterValidDrop(target, e, id);
3736         }
3737     },
3738
3739     // private
3740     getRepairXY : function(e, data){
3741         return this.el.getXY();  
3742     },
3743
3744     // private
3745     onInvalidDrop : function(target, e, id){
3746         this.beforeInvalidDrop(target, e, id);
3747         if(this.cachedTarget){
3748             if(this.cachedTarget.isNotifyTarget){
3749                 this.cachedTarget.notifyOut(this, e, this.dragData);
3750             }
3751             this.cacheTarget = null;
3752         }
3753         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3754
3755         if(this.afterInvalidDrop){
3756             /**
3757              * An empty function by default, but provided so that you can perform a custom action
3758              * after an invalid drop has occurred by providing an implementation.
3759              * @param {Event} e The event object
3760              * @param {String} id The id of the dropped element
3761              * @method afterInvalidDrop
3762              */
3763             this.afterInvalidDrop(e, id);
3764         }
3765     },
3766
3767     // private
3768     afterRepair : function(){
3769         if(Roo.enableFx){
3770             this.el.highlight(this.hlColor || "c3daf9");
3771         }
3772         this.dragging = false;
3773     },
3774
3775     /**
3776      * An empty function by default, but provided so that you can perform a custom action after an invalid
3777      * drop has occurred.
3778      * @param {Roo.dd.DragDrop} target The drop target
3779      * @param {Event} e The event object
3780      * @param {String} id The id of the dragged element
3781      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3782      */
3783     beforeInvalidDrop : function(target, e, id){
3784         return true;
3785     },
3786
3787     // private
3788     handleMouseDown : function(e){
3789         if(this.dragging) {
3790             return;
3791         }
3792         var data = this.getDragData(e);
3793         if(data && this.onBeforeDrag(data, e) !== false){
3794             this.dragData = data;
3795             this.proxy.stop();
3796             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3797         } 
3798     },
3799
3800     /**
3801      * An empty function by default, but provided so that you can perform a custom action before the initial
3802      * drag event begins and optionally cancel it.
3803      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3804      * @param {Event} e The event object
3805      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3806      */
3807     onBeforeDrag : function(data, e){
3808         return true;
3809     },
3810
3811     /**
3812      * An empty function by default, but provided so that you can perform a custom action once the initial
3813      * drag event has begun.  The drag cannot be canceled from this function.
3814      * @param {Number} x The x position of the click on the dragged object
3815      * @param {Number} y The y position of the click on the dragged object
3816      */
3817     onStartDrag : Roo.emptyFn,
3818
3819     // private - YUI override
3820     startDrag : function(x, y){
3821         this.proxy.reset();
3822         this.dragging = true;
3823         this.proxy.update("");
3824         this.onInitDrag(x, y);
3825         this.proxy.show();
3826     },
3827
3828     // private
3829     onInitDrag : function(x, y){
3830         var clone = this.el.dom.cloneNode(true);
3831         clone.id = Roo.id(); // prevent duplicate ids
3832         this.proxy.update(clone);
3833         this.onStartDrag(x, y);
3834         return true;
3835     },
3836
3837     /**
3838      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3839      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3840      */
3841     getProxy : function(){
3842         return this.proxy;  
3843     },
3844
3845     /**
3846      * Hides the drag source's {@link Roo.dd.StatusProxy}
3847      */
3848     hideProxy : function(){
3849         this.proxy.hide();  
3850         this.proxy.reset(true);
3851         this.dragging = false;
3852     },
3853
3854     // private
3855     triggerCacheRefresh : function(){
3856         Roo.dd.DDM.refreshCache(this.groups);
3857     },
3858
3859     // private - override to prevent hiding
3860     b4EndDrag: function(e) {
3861     },
3862
3863     // private - override to prevent moving
3864     endDrag : function(e){
3865         this.onEndDrag(this.dragData, e);
3866     },
3867
3868     // private
3869     onEndDrag : function(data, e){
3870     },
3871     
3872     // private - pin to cursor
3873     autoOffset : function(x, y) {
3874         this.setDelta(-12, -20);
3875     }    
3876 });/*
3877  * Based on:
3878  * Ext JS Library 1.1.1
3879  * Copyright(c) 2006-2007, Ext JS, LLC.
3880  *
3881  * Originally Released Under LGPL - original licence link has changed is not relivant.
3882  *
3883  * Fork - LGPL
3884  * <script type="text/javascript">
3885  */
3886
3887
3888 /**
3889  * @class Roo.dd.DropTarget
3890  * @extends Roo.dd.DDTarget
3891  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3892  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3893  * @constructor
3894  * @param {String/HTMLElement/Element} el The container element
3895  * @param {Object} config
3896  */
3897 Roo.dd.DropTarget = function(el, config){
3898     this.el = Roo.get(el);
3899     
3900     var listeners = false; ;
3901     if (config && config.listeners) {
3902         listeners= config.listeners;
3903         delete config.listeners;
3904     }
3905     Roo.apply(this, config);
3906     
3907     if(this.containerScroll){
3908         Roo.dd.ScrollManager.register(this.el);
3909     }
3910     this.addEvents( {
3911          /**
3912          * @scope Roo.dd.DropTarget
3913          */
3914          
3915          /**
3916          * @event enter
3917          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3918          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3919          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3920          * 
3921          * IMPORTANT : it should set this.overClass and this.dropAllowed
3922          * 
3923          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3924          * @param {Event} e The event
3925          * @param {Object} data An object containing arbitrary data supplied by the drag source
3926          */
3927         "enter" : true,
3928         
3929          /**
3930          * @event over
3931          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3932          * This method will be called on every mouse movement while the drag source is over the drop target.
3933          * This default implementation simply returns the dropAllowed config value.
3934          * 
3935          * IMPORTANT : it should set this.dropAllowed
3936          * 
3937          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3938          * @param {Event} e The event
3939          * @param {Object} data An object containing arbitrary data supplied by the drag source
3940          
3941          */
3942         "over" : true,
3943         /**
3944          * @event out
3945          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3946          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3947          * overClass (if any) from the drop element.
3948          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3949          * @param {Event} e The event
3950          * @param {Object} data An object containing arbitrary data supplied by the drag source
3951          */
3952          "out" : true,
3953          
3954         /**
3955          * @event drop
3956          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3957          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3958          * implementation that does something to process the drop event and returns true so that the drag source's
3959          * repair action does not run.
3960          * 
3961          * IMPORTANT : it should set this.success
3962          * 
3963          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3964          * @param {Event} e The event
3965          * @param {Object} data An object containing arbitrary data supplied by the drag source
3966         */
3967          "drop" : true
3968     });
3969             
3970      
3971     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3972         this.el.dom, 
3973         this.ddGroup || this.group,
3974         {
3975             isTarget: true,
3976             listeners : listeners || {} 
3977            
3978         
3979         }
3980     );
3981
3982 };
3983
3984 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3985     /**
3986      * @cfg {String} overClass
3987      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3988      */
3989      /**
3990      * @cfg {String} ddGroup
3991      * The drag drop group to handle drop events for
3992      */
3993      
3994     /**
3995      * @cfg {String} dropAllowed
3996      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3997      */
3998     dropAllowed : "x-dd-drop-ok",
3999     /**
4000      * @cfg {String} dropNotAllowed
4001      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4002      */
4003     dropNotAllowed : "x-dd-drop-nodrop",
4004     /**
4005      * @cfg {boolean} success
4006      * set this after drop listener.. 
4007      */
4008     success : false,
4009     /**
4010      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4011      * if the drop point is valid for over/enter..
4012      */
4013     valid : false,
4014     // private
4015     isTarget : true,
4016
4017     // private
4018     isNotifyTarget : true,
4019     
4020     /**
4021      * @hide
4022      */
4023     notifyEnter : function(dd, e, data)
4024     {
4025         this.valid = true;
4026         this.fireEvent('enter', dd, e, data);
4027         if(this.overClass){
4028             this.el.addClass(this.overClass);
4029         }
4030         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4031             this.valid ? this.dropAllowed : this.dropNotAllowed
4032         );
4033     },
4034
4035     /**
4036      * @hide
4037      */
4038     notifyOver : function(dd, e, data)
4039     {
4040         this.valid = true;
4041         this.fireEvent('over', dd, e, data);
4042         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4043             this.valid ? this.dropAllowed : this.dropNotAllowed
4044         );
4045     },
4046
4047     /**
4048      * @hide
4049      */
4050     notifyOut : function(dd, e, data)
4051     {
4052         this.fireEvent('out', dd, e, data);
4053         if(this.overClass){
4054             this.el.removeClass(this.overClass);
4055         }
4056     },
4057
4058     /**
4059      * @hide
4060      */
4061     notifyDrop : function(dd, e, data)
4062     {
4063         this.success = false;
4064         this.fireEvent('drop', dd, e, data);
4065         return this.success;
4066     }
4067 });/*
4068  * Based on:
4069  * Ext JS Library 1.1.1
4070  * Copyright(c) 2006-2007, Ext JS, LLC.
4071  *
4072  * Originally Released Under LGPL - original licence link has changed is not relivant.
4073  *
4074  * Fork - LGPL
4075  * <script type="text/javascript">
4076  */
4077
4078
4079 /**
4080  * @class Roo.dd.DragZone
4081  * @extends Roo.dd.DragSource
4082  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4083  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4084  * @constructor
4085  * @param {String/HTMLElement/Element} el The container element
4086  * @param {Object} config
4087  */
4088 Roo.dd.DragZone = function(el, config){
4089     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4090     if(this.containerScroll){
4091         Roo.dd.ScrollManager.register(this.el);
4092     }
4093 };
4094
4095 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4096     /**
4097      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4098      * for auto scrolling during drag operations.
4099      */
4100     /**
4101      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4102      * method after a failed drop (defaults to "c3daf9" - light blue)
4103      */
4104
4105     /**
4106      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4107      * for a valid target to drag based on the mouse down. Override this method
4108      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4109      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4110      * @param {EventObject} e The mouse down event
4111      * @return {Object} The dragData
4112      */
4113     getDragData : function(e){
4114         return Roo.dd.Registry.getHandleFromEvent(e);
4115     },
4116     
4117     /**
4118      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4119      * this.dragData.ddel
4120      * @param {Number} x The x position of the click on the dragged object
4121      * @param {Number} y The y position of the click on the dragged object
4122      * @return {Boolean} true to continue the drag, false to cancel
4123      */
4124     onInitDrag : function(x, y){
4125         this.proxy.update(this.dragData.ddel.cloneNode(true));
4126         this.onStartDrag(x, y);
4127         return true;
4128     },
4129     
4130     /**
4131      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4132      */
4133     afterRepair : function(){
4134         if(Roo.enableFx){
4135             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4136         }
4137         this.dragging = false;
4138     },
4139
4140     /**
4141      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4142      * the XY of this.dragData.ddel
4143      * @param {EventObject} e The mouse up event
4144      * @return {Array} The xy location (e.g. [100, 200])
4145      */
4146     getRepairXY : function(e){
4147         return Roo.Element.fly(this.dragData.ddel).getXY();  
4148     }
4149 });/*
4150  * Based on:
4151  * Ext JS Library 1.1.1
4152  * Copyright(c) 2006-2007, Ext JS, LLC.
4153  *
4154  * Originally Released Under LGPL - original licence link has changed is not relivant.
4155  *
4156  * Fork - LGPL
4157  * <script type="text/javascript">
4158  */
4159 /**
4160  * @class Roo.dd.DropZone
4161  * @extends Roo.dd.DropTarget
4162  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4163  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4164  * @constructor
4165  * @param {String/HTMLElement/Element} el The container element
4166  * @param {Object} config
4167  */
4168 Roo.dd.DropZone = function(el, config){
4169     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4170 };
4171
4172 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4173     /**
4174      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4175      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4176      * provide your own custom lookup.
4177      * @param {Event} e The event
4178      * @return {Object} data The custom data
4179      */
4180     getTargetFromEvent : function(e){
4181         return Roo.dd.Registry.getTargetFromEvent(e);
4182     },
4183
4184     /**
4185      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4186      * that it has registered.  This method has no default implementation and should be overridden to provide
4187      * node-specific processing if necessary.
4188      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4189      * {@link #getTargetFromEvent} for this node)
4190      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4191      * @param {Event} e The event
4192      * @param {Object} data An object containing arbitrary data supplied by the drag source
4193      */
4194     onNodeEnter : function(n, dd, e, data){
4195         
4196     },
4197
4198     /**
4199      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4200      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4201      * overridden to provide the proper feedback.
4202      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4203      * {@link #getTargetFromEvent} for this node)
4204      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4205      * @param {Event} e The event
4206      * @param {Object} data An object containing arbitrary data supplied by the drag source
4207      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4208      * underlying {@link Roo.dd.StatusProxy} can be updated
4209      */
4210     onNodeOver : function(n, dd, e, data){
4211         return this.dropAllowed;
4212     },
4213
4214     /**
4215      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4216      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4217      * node-specific processing if necessary.
4218      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4219      * {@link #getTargetFromEvent} for this node)
4220      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4221      * @param {Event} e The event
4222      * @param {Object} data An object containing arbitrary data supplied by the drag source
4223      */
4224     onNodeOut : function(n, dd, e, data){
4225         
4226     },
4227
4228     /**
4229      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4230      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4231      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4232      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4233      * {@link #getTargetFromEvent} for this node)
4234      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4235      * @param {Event} e The event
4236      * @param {Object} data An object containing arbitrary data supplied by the drag source
4237      * @return {Boolean} True if the drop was valid, else false
4238      */
4239     onNodeDrop : function(n, dd, e, data){
4240         return false;
4241     },
4242
4243     /**
4244      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4245      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4246      * it should be overridden to provide the proper feedback if necessary.
4247      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4248      * @param {Event} e The event
4249      * @param {Object} data An object containing arbitrary data supplied by the drag source
4250      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4251      * underlying {@link Roo.dd.StatusProxy} can be updated
4252      */
4253     onContainerOver : function(dd, e, data){
4254         return this.dropNotAllowed;
4255     },
4256
4257     /**
4258      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4259      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4260      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4261      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4262      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4263      * @param {Event} e The event
4264      * @param {Object} data An object containing arbitrary data supplied by the drag source
4265      * @return {Boolean} True if the drop was valid, else false
4266      */
4267     onContainerDrop : function(dd, e, data){
4268         return false;
4269     },
4270
4271     /**
4272      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4273      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4274      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4275      * you should override this method and provide a custom implementation.
4276      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4277      * @param {Event} e The event
4278      * @param {Object} data An object containing arbitrary data supplied by the drag source
4279      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4280      * underlying {@link Roo.dd.StatusProxy} can be updated
4281      */
4282     notifyEnter : function(dd, e, data){
4283         return this.dropNotAllowed;
4284     },
4285
4286     /**
4287      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4288      * This method will be called on every mouse movement while the drag source is over the drop zone.
4289      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4290      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4291      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4292      * registered node, it will call {@link #onContainerOver}.
4293      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4294      * @param {Event} e The event
4295      * @param {Object} data An object containing arbitrary data supplied by the drag source
4296      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4297      * underlying {@link Roo.dd.StatusProxy} can be updated
4298      */
4299     notifyOver : function(dd, e, data){
4300         var n = this.getTargetFromEvent(e);
4301         if(!n){ // not over valid drop target
4302             if(this.lastOverNode){
4303                 this.onNodeOut(this.lastOverNode, dd, e, data);
4304                 this.lastOverNode = null;
4305             }
4306             return this.onContainerOver(dd, e, data);
4307         }
4308         if(this.lastOverNode != n){
4309             if(this.lastOverNode){
4310                 this.onNodeOut(this.lastOverNode, dd, e, data);
4311             }
4312             this.onNodeEnter(n, dd, e, data);
4313             this.lastOverNode = n;
4314         }
4315         return this.onNodeOver(n, dd, e, data);
4316     },
4317
4318     /**
4319      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4320      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4321      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4322      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4323      * @param {Event} e The event
4324      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4325      */
4326     notifyOut : function(dd, e, data){
4327         if(this.lastOverNode){
4328             this.onNodeOut(this.lastOverNode, dd, e, data);
4329             this.lastOverNode = null;
4330         }
4331     },
4332
4333     /**
4334      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4335      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4336      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4337      * otherwise it will call {@link #onContainerDrop}.
4338      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4339      * @param {Event} e The event
4340      * @param {Object} data An object containing arbitrary data supplied by the drag source
4341      * @return {Boolean} True if the drop was valid, else false
4342      */
4343     notifyDrop : function(dd, e, data){
4344         if(this.lastOverNode){
4345             this.onNodeOut(this.lastOverNode, dd, e, data);
4346             this.lastOverNode = null;
4347         }
4348         var n = this.getTargetFromEvent(e);
4349         return n ?
4350             this.onNodeDrop(n, dd, e, data) :
4351             this.onContainerDrop(dd, e, data);
4352     },
4353
4354     // private
4355     triggerCacheRefresh : function(){
4356         Roo.dd.DDM.refreshCache(this.groups);
4357     }  
4358 });/*
4359  * Based on:
4360  * Ext JS Library 1.1.1
4361  * Copyright(c) 2006-2007, Ext JS, LLC.
4362  *
4363  * Originally Released Under LGPL - original licence link has changed is not relivant.
4364  *
4365  * Fork - LGPL
4366  * <script type="text/javascript">
4367  */
4368
4369
4370 /**
4371  * @class Roo.data.SortTypes
4372  * @singleton
4373  * Defines the default sorting (casting?) comparison functions used when sorting data.
4374  */
4375 Roo.data.SortTypes = {
4376     /**
4377      * Default sort that does nothing
4378      * @param {Mixed} s The value being converted
4379      * @return {Mixed} The comparison value
4380      */
4381     none : function(s){
4382         return s;
4383     },
4384     
4385     /**
4386      * The regular expression used to strip tags
4387      * @type {RegExp}
4388      * @property
4389      */
4390     stripTagsRE : /<\/?[^>]+>/gi,
4391     
4392     /**
4393      * Strips all HTML tags to sort on text only
4394      * @param {Mixed} s The value being converted
4395      * @return {String} The comparison value
4396      */
4397     asText : function(s){
4398         return String(s).replace(this.stripTagsRE, "");
4399     },
4400     
4401     /**
4402      * Strips all HTML tags to sort on text only - Case insensitive
4403      * @param {Mixed} s The value being converted
4404      * @return {String} The comparison value
4405      */
4406     asUCText : function(s){
4407         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4408     },
4409     
4410     /**
4411      * Case insensitive string
4412      * @param {Mixed} s The value being converted
4413      * @return {String} The comparison value
4414      */
4415     asUCString : function(s) {
4416         return String(s).toUpperCase();
4417     },
4418     
4419     /**
4420      * Date sorting
4421      * @param {Mixed} s The value being converted
4422      * @return {Number} The comparison value
4423      */
4424     asDate : function(s) {
4425         if(!s){
4426             return 0;
4427         }
4428         if(s instanceof Date){
4429             return s.getTime();
4430         }
4431         return Date.parse(String(s));
4432     },
4433     
4434     /**
4435      * Float sorting
4436      * @param {Mixed} s The value being converted
4437      * @return {Float} The comparison value
4438      */
4439     asFloat : function(s) {
4440         var val = parseFloat(String(s).replace(/,/g, ""));
4441         if(isNaN(val)) val = 0;
4442         return val;
4443     },
4444     
4445     /**
4446      * Integer sorting
4447      * @param {Mixed} s The value being converted
4448      * @return {Number} The comparison value
4449      */
4450     asInt : function(s) {
4451         var val = parseInt(String(s).replace(/,/g, ""));
4452         if(isNaN(val)) val = 0;
4453         return val;
4454     }
4455 };/*
4456  * Based on:
4457  * Ext JS Library 1.1.1
4458  * Copyright(c) 2006-2007, Ext JS, LLC.
4459  *
4460  * Originally Released Under LGPL - original licence link has changed is not relivant.
4461  *
4462  * Fork - LGPL
4463  * <script type="text/javascript">
4464  */
4465
4466 /**
4467 * @class Roo.data.Record
4468  * Instances of this class encapsulate both record <em>definition</em> information, and record
4469  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4470  * to access Records cached in an {@link Roo.data.Store} object.<br>
4471  * <p>
4472  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4473  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4474  * objects.<br>
4475  * <p>
4476  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4477  * @constructor
4478  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4479  * {@link #create}. The parameters are the same.
4480  * @param {Array} data An associative Array of data values keyed by the field name.
4481  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4482  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4483  * not specified an integer id is generated.
4484  */
4485 Roo.data.Record = function(data, id){
4486     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4487     this.data = data;
4488 };
4489
4490 /**
4491  * Generate a constructor for a specific record layout.
4492  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4493  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4494  * Each field definition object may contain the following properties: <ul>
4495  * <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,
4496  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4497  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4498  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4499  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4500  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4501  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4502  * this may be omitted.</p></li>
4503  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4504  * <ul><li>auto (Default, implies no conversion)</li>
4505  * <li>string</li>
4506  * <li>int</li>
4507  * <li>float</li>
4508  * <li>boolean</li>
4509  * <li>date</li></ul></p></li>
4510  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4511  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4512  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4513  * by the Reader into an object that will be stored in the Record. It is passed the
4514  * following parameters:<ul>
4515  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4516  * </ul></p></li>
4517  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4518  * </ul>
4519  * <br>usage:<br><pre><code>
4520 var TopicRecord = Roo.data.Record.create(
4521     {name: 'title', mapping: 'topic_title'},
4522     {name: 'author', mapping: 'username'},
4523     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4524     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4525     {name: 'lastPoster', mapping: 'user2'},
4526     {name: 'excerpt', mapping: 'post_text'}
4527 );
4528
4529 var myNewRecord = new TopicRecord({
4530     title: 'Do my job please',
4531     author: 'noobie',
4532     totalPosts: 1,
4533     lastPost: new Date(),
4534     lastPoster: 'Animal',
4535     excerpt: 'No way dude!'
4536 });
4537 myStore.add(myNewRecord);
4538 </code></pre>
4539  * @method create
4540  * @static
4541  */
4542 Roo.data.Record.create = function(o){
4543     var f = function(){
4544         f.superclass.constructor.apply(this, arguments);
4545     };
4546     Roo.extend(f, Roo.data.Record);
4547     var p = f.prototype;
4548     p.fields = new Roo.util.MixedCollection(false, function(field){
4549         return field.name;
4550     });
4551     for(var i = 0, len = o.length; i < len; i++){
4552         p.fields.add(new Roo.data.Field(o[i]));
4553     }
4554     f.getField = function(name){
4555         return p.fields.get(name);  
4556     };
4557     return f;
4558 };
4559
4560 Roo.data.Record.AUTO_ID = 1000;
4561 Roo.data.Record.EDIT = 'edit';
4562 Roo.data.Record.REJECT = 'reject';
4563 Roo.data.Record.COMMIT = 'commit';
4564
4565 Roo.data.Record.prototype = {
4566     /**
4567      * Readonly flag - true if this record has been modified.
4568      * @type Boolean
4569      */
4570     dirty : false,
4571     editing : false,
4572     error: null,
4573     modified: null,
4574
4575     // private
4576     join : function(store){
4577         this.store = store;
4578     },
4579
4580     /**
4581      * Set the named field to the specified value.
4582      * @param {String} name The name of the field to set.
4583      * @param {Object} value The value to set the field to.
4584      */
4585     set : function(name, value){
4586         if(this.data[name] == value){
4587             return;
4588         }
4589         this.dirty = true;
4590         if(!this.modified){
4591             this.modified = {};
4592         }
4593         if(typeof this.modified[name] == 'undefined'){
4594             this.modified[name] = this.data[name];
4595         }
4596         this.data[name] = value;
4597         if(!this.editing && this.store){
4598             this.store.afterEdit(this);
4599         }       
4600     },
4601
4602     /**
4603      * Get the value of the named field.
4604      * @param {String} name The name of the field to get the value of.
4605      * @return {Object} The value of the field.
4606      */
4607     get : function(name){
4608         return this.data[name]; 
4609     },
4610
4611     // private
4612     beginEdit : function(){
4613         this.editing = true;
4614         this.modified = {}; 
4615     },
4616
4617     // private
4618     cancelEdit : function(){
4619         this.editing = false;
4620         delete this.modified;
4621     },
4622
4623     // private
4624     endEdit : function(){
4625         this.editing = false;
4626         if(this.dirty && this.store){
4627             this.store.afterEdit(this);
4628         }
4629     },
4630
4631     /**
4632      * Usually called by the {@link Roo.data.Store} which owns the Record.
4633      * Rejects all changes made to the Record since either creation, or the last commit operation.
4634      * Modified fields are reverted to their original values.
4635      * <p>
4636      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4637      * of reject operations.
4638      */
4639     reject : function(){
4640         var m = this.modified;
4641         for(var n in m){
4642             if(typeof m[n] != "function"){
4643                 this.data[n] = m[n];
4644             }
4645         }
4646         this.dirty = false;
4647         delete this.modified;
4648         this.editing = false;
4649         if(this.store){
4650             this.store.afterReject(this);
4651         }
4652     },
4653
4654     /**
4655      * Usually called by the {@link Roo.data.Store} which owns the Record.
4656      * Commits all changes made to the Record since either creation, or the last commit operation.
4657      * <p>
4658      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4659      * of commit operations.
4660      */
4661     commit : function(){
4662         this.dirty = false;
4663         delete this.modified;
4664         this.editing = false;
4665         if(this.store){
4666             this.store.afterCommit(this);
4667         }
4668     },
4669
4670     // private
4671     hasError : function(){
4672         return this.error != null;
4673     },
4674
4675     // private
4676     clearError : function(){
4677         this.error = null;
4678     },
4679
4680     /**
4681      * Creates a copy of this record.
4682      * @param {String} id (optional) A new record id if you don't want to use this record's id
4683      * @return {Record}
4684      */
4685     copy : function(newId) {
4686         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4687     }
4688 };/*
4689  * Based on:
4690  * Ext JS Library 1.1.1
4691  * Copyright(c) 2006-2007, Ext JS, LLC.
4692  *
4693  * Originally Released Under LGPL - original licence link has changed is not relivant.
4694  *
4695  * Fork - LGPL
4696  * <script type="text/javascript">
4697  */
4698
4699
4700
4701 /**
4702  * @class Roo.data.Store
4703  * @extends Roo.util.Observable
4704  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4705  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4706  * <p>
4707  * 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
4708  * has no knowledge of the format of the data returned by the Proxy.<br>
4709  * <p>
4710  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4711  * instances from the data object. These records are cached and made available through accessor functions.
4712  * @constructor
4713  * Creates a new Store.
4714  * @param {Object} config A config object containing the objects needed for the Store to access data,
4715  * and read the data into Records.
4716  */
4717 Roo.data.Store = function(config){
4718     this.data = new Roo.util.MixedCollection(false);
4719     this.data.getKey = function(o){
4720         return o.id;
4721     };
4722     this.baseParams = {};
4723     // private
4724     this.paramNames = {
4725         "start" : "start",
4726         "limit" : "limit",
4727         "sort" : "sort",
4728         "dir" : "dir",
4729         "multisort" : "_multisort"
4730     };
4731
4732     if(config && config.data){
4733         this.inlineData = config.data;
4734         delete config.data;
4735     }
4736
4737     Roo.apply(this, config);
4738     
4739     if(this.reader){ // reader passed
4740         this.reader = Roo.factory(this.reader, Roo.data);
4741         this.reader.xmodule = this.xmodule || false;
4742         if(!this.recordType){
4743             this.recordType = this.reader.recordType;
4744         }
4745         if(this.reader.onMetaChange){
4746             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4747         }
4748     }
4749
4750     if(this.recordType){
4751         this.fields = this.recordType.prototype.fields;
4752     }
4753     this.modified = [];
4754
4755     this.addEvents({
4756         /**
4757          * @event datachanged
4758          * Fires when the data cache has changed, and a widget which is using this Store
4759          * as a Record cache should refresh its view.
4760          * @param {Store} this
4761          */
4762         datachanged : true,
4763         /**
4764          * @event metachange
4765          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4766          * @param {Store} this
4767          * @param {Object} meta The JSON metadata
4768          */
4769         metachange : true,
4770         /**
4771          * @event add
4772          * Fires when Records have been added to the Store
4773          * @param {Store} this
4774          * @param {Roo.data.Record[]} records The array of Records added
4775          * @param {Number} index The index at which the record(s) were added
4776          */
4777         add : true,
4778         /**
4779          * @event remove
4780          * Fires when a Record has been removed from the Store
4781          * @param {Store} this
4782          * @param {Roo.data.Record} record The Record that was removed
4783          * @param {Number} index The index at which the record was removed
4784          */
4785         remove : true,
4786         /**
4787          * @event update
4788          * Fires when a Record has been updated
4789          * @param {Store} this
4790          * @param {Roo.data.Record} record The Record that was updated
4791          * @param {String} operation The update operation being performed.  Value may be one of:
4792          * <pre><code>
4793  Roo.data.Record.EDIT
4794  Roo.data.Record.REJECT
4795  Roo.data.Record.COMMIT
4796          * </code></pre>
4797          */
4798         update : true,
4799         /**
4800          * @event clear
4801          * Fires when the data cache has been cleared.
4802          * @param {Store} this
4803          */
4804         clear : true,
4805         /**
4806          * @event beforeload
4807          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4808          * the load action will be canceled.
4809          * @param {Store} this
4810          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4811          */
4812         beforeload : true,
4813         /**
4814          * @event load
4815          * Fires after a new set of Records has been loaded.
4816          * @param {Store} this
4817          * @param {Roo.data.Record[]} records The Records that were loaded
4818          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4819          */
4820         load : true,
4821         /**
4822          * @event loadexception
4823          * Fires if an exception occurs in the Proxy during loading.
4824          * Called with the signature of the Proxy's "loadexception" event.
4825          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4826          * 
4827          * @param {Proxy} 
4828          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4829          * @param {Object} load options 
4830          * @param {Object} jsonData from your request (normally this contains the Exception)
4831          */
4832         loadexception : true
4833     });
4834     
4835     if(this.proxy){
4836         this.proxy = Roo.factory(this.proxy, Roo.data);
4837         this.proxy.xmodule = this.xmodule || false;
4838         this.relayEvents(this.proxy,  ["loadexception"]);
4839     }
4840     this.sortToggle = {};
4841     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4842
4843     Roo.data.Store.superclass.constructor.call(this);
4844
4845     if(this.inlineData){
4846         this.loadData(this.inlineData);
4847         delete this.inlineData;
4848     }
4849 };
4850 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4851      /**
4852     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4853     * without a remote query - used by combo/forms at present.
4854     */
4855     
4856     /**
4857     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4858     */
4859     /**
4860     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4861     */
4862     /**
4863     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4864     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4865     */
4866     /**
4867     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4868     * on any HTTP request
4869     */
4870     /**
4871     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4872     */
4873     /**
4874     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4875     */
4876     multiSort: false,
4877     /**
4878     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4879     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4880     */
4881     remoteSort : false,
4882
4883     /**
4884     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4885      * loaded or when a record is removed. (defaults to false).
4886     */
4887     pruneModifiedRecords : false,
4888
4889     // private
4890     lastOptions : null,
4891
4892     /**
4893      * Add Records to the Store and fires the add event.
4894      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4895      */
4896     add : function(records){
4897         records = [].concat(records);
4898         for(var i = 0, len = records.length; i < len; i++){
4899             records[i].join(this);
4900         }
4901         var index = this.data.length;
4902         this.data.addAll(records);
4903         this.fireEvent("add", this, records, index);
4904     },
4905
4906     /**
4907      * Remove a Record from the Store and fires the remove event.
4908      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4909      */
4910     remove : function(record){
4911         var index = this.data.indexOf(record);
4912         this.data.removeAt(index);
4913         if(this.pruneModifiedRecords){
4914             this.modified.remove(record);
4915         }
4916         this.fireEvent("remove", this, record, index);
4917     },
4918
4919     /**
4920      * Remove all Records from the Store and fires the clear event.
4921      */
4922     removeAll : function(){
4923         this.data.clear();
4924         if(this.pruneModifiedRecords){
4925             this.modified = [];
4926         }
4927         this.fireEvent("clear", this);
4928     },
4929
4930     /**
4931      * Inserts Records to the Store at the given index and fires the add event.
4932      * @param {Number} index The start index at which to insert the passed Records.
4933      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4934      */
4935     insert : function(index, records){
4936         records = [].concat(records);
4937         for(var i = 0, len = records.length; i < len; i++){
4938             this.data.insert(index, records[i]);
4939             records[i].join(this);
4940         }
4941         this.fireEvent("add", this, records, index);
4942     },
4943
4944     /**
4945      * Get the index within the cache of the passed Record.
4946      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4947      * @return {Number} The index of the passed Record. Returns -1 if not found.
4948      */
4949     indexOf : function(record){
4950         return this.data.indexOf(record);
4951     },
4952
4953     /**
4954      * Get the index within the cache of the Record with the passed id.
4955      * @param {String} id The id of the Record to find.
4956      * @return {Number} The index of the Record. Returns -1 if not found.
4957      */
4958     indexOfId : function(id){
4959         return this.data.indexOfKey(id);
4960     },
4961
4962     /**
4963      * Get the Record with the specified id.
4964      * @param {String} id The id of the Record to find.
4965      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4966      */
4967     getById : function(id){
4968         return this.data.key(id);
4969     },
4970
4971     /**
4972      * Get the Record at the specified index.
4973      * @param {Number} index The index of the Record to find.
4974      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4975      */
4976     getAt : function(index){
4977         return this.data.itemAt(index);
4978     },
4979
4980     /**
4981      * Returns a range of Records between specified indices.
4982      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4983      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4984      * @return {Roo.data.Record[]} An array of Records
4985      */
4986     getRange : function(start, end){
4987         return this.data.getRange(start, end);
4988     },
4989
4990     // private
4991     storeOptions : function(o){
4992         o = Roo.apply({}, o);
4993         delete o.callback;
4994         delete o.scope;
4995         this.lastOptions = o;
4996     },
4997
4998     /**
4999      * Loads the Record cache from the configured Proxy using the configured Reader.
5000      * <p>
5001      * If using remote paging, then the first load call must specify the <em>start</em>
5002      * and <em>limit</em> properties in the options.params property to establish the initial
5003      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5004      * <p>
5005      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5006      * and this call will return before the new data has been loaded. Perform any post-processing
5007      * in a callback function, or in a "load" event handler.</strong>
5008      * <p>
5009      * @param {Object} options An object containing properties which control loading options:<ul>
5010      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5011      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5012      * passed the following arguments:<ul>
5013      * <li>r : Roo.data.Record[]</li>
5014      * <li>options: Options object from the load call</li>
5015      * <li>success: Boolean success indicator</li></ul></li>
5016      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5017      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5018      * </ul>
5019      */
5020     load : function(options){
5021         options = options || {};
5022         if(this.fireEvent("beforeload", this, options) !== false){
5023             this.storeOptions(options);
5024             var p = Roo.apply(options.params || {}, this.baseParams);
5025             // if meta was not loaded from remote source.. try requesting it.
5026             if (!this.reader.metaFromRemote) {
5027                 p._requestMeta = 1;
5028             }
5029             if(this.sortInfo && this.remoteSort){
5030                 var pn = this.paramNames;
5031                 p[pn["sort"]] = this.sortInfo.field;
5032                 p[pn["dir"]] = this.sortInfo.direction;
5033             }
5034             if (this.multiSort) {
5035                 var pn = this.paramNames;
5036                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5037             }
5038             
5039             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5040         }
5041     },
5042
5043     /**
5044      * Reloads the Record cache from the configured Proxy using the configured Reader and
5045      * the options from the last load operation performed.
5046      * @param {Object} options (optional) An object containing properties which may override the options
5047      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5048      * the most recently used options are reused).
5049      */
5050     reload : function(options){
5051         this.load(Roo.applyIf(options||{}, this.lastOptions));
5052     },
5053
5054     // private
5055     // Called as a callback by the Reader during a load operation.
5056     loadRecords : function(o, options, success){
5057         if(!o || success === false){
5058             if(success !== false){
5059                 this.fireEvent("load", this, [], options);
5060             }
5061             if(options.callback){
5062                 options.callback.call(options.scope || this, [], options, false);
5063             }
5064             return;
5065         }
5066         // if data returned failure - throw an exception.
5067         if (o.success === false) {
5068             // show a message if no listener is registered.
5069             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
5070                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
5071             }
5072             // loadmask wil be hooked into this..
5073             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5074             return;
5075         }
5076         var r = o.records, t = o.totalRecords || r.length;
5077         if(!options || options.add !== true){
5078             if(this.pruneModifiedRecords){
5079                 this.modified = [];
5080             }
5081             for(var i = 0, len = r.length; i < len; i++){
5082                 r[i].join(this);
5083             }
5084             if(this.snapshot){
5085                 this.data = this.snapshot;
5086                 delete this.snapshot;
5087             }
5088             this.data.clear();
5089             this.data.addAll(r);
5090             this.totalLength = t;
5091             this.applySort();
5092             this.fireEvent("datachanged", this);
5093         }else{
5094             this.totalLength = Math.max(t, this.data.length+r.length);
5095             this.add(r);
5096         }
5097         this.fireEvent("load", this, r, options);
5098         if(options.callback){
5099             options.callback.call(options.scope || this, r, options, true);
5100         }
5101     },
5102
5103
5104     /**
5105      * Loads data from a passed data block. A Reader which understands the format of the data
5106      * must have been configured in the constructor.
5107      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5108      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5109      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5110      */
5111     loadData : function(o, append){
5112         var r = this.reader.readRecords(o);
5113         this.loadRecords(r, {add: append}, true);
5114     },
5115
5116     /**
5117      * Gets the number of cached records.
5118      * <p>
5119      * <em>If using paging, this may not be the total size of the dataset. If the data object
5120      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5121      * the data set size</em>
5122      */
5123     getCount : function(){
5124         return this.data.length || 0;
5125     },
5126
5127     /**
5128      * Gets the total number of records in the dataset as returned by the server.
5129      * <p>
5130      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5131      * the dataset size</em>
5132      */
5133     getTotalCount : function(){
5134         return this.totalLength || 0;
5135     },
5136
5137     /**
5138      * Returns the sort state of the Store as an object with two properties:
5139      * <pre><code>
5140  field {String} The name of the field by which the Records are sorted
5141  direction {String} The sort order, "ASC" or "DESC"
5142      * </code></pre>
5143      */
5144     getSortState : function(){
5145         return this.sortInfo;
5146     },
5147
5148     // private
5149     applySort : function(){
5150         if(this.sortInfo && !this.remoteSort){
5151             var s = this.sortInfo, f = s.field;
5152             var st = this.fields.get(f).sortType;
5153             var fn = function(r1, r2){
5154                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5155                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5156             };
5157             this.data.sort(s.direction, fn);
5158             if(this.snapshot && this.snapshot != this.data){
5159                 this.snapshot.sort(s.direction, fn);
5160             }
5161         }
5162     },
5163
5164     /**
5165      * Sets the default sort column and order to be used by the next load operation.
5166      * @param {String} fieldName The name of the field to sort by.
5167      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5168      */
5169     setDefaultSort : function(field, dir){
5170         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5171     },
5172
5173     /**
5174      * Sort the Records.
5175      * If remote sorting is used, the sort is performed on the server, and the cache is
5176      * reloaded. If local sorting is used, the cache is sorted internally.
5177      * @param {String} fieldName The name of the field to sort by.
5178      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5179      */
5180     sort : function(fieldName, dir){
5181         var f = this.fields.get(fieldName);
5182         if(!dir){
5183             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5184             
5185             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5186                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5187             }else{
5188                 dir = f.sortDir;
5189             }
5190         }
5191         this.sortToggle[f.name] = dir;
5192         this.sortInfo = {field: f.name, direction: dir};
5193         if(!this.remoteSort){
5194             this.applySort();
5195             this.fireEvent("datachanged", this);
5196         }else{
5197             this.load(this.lastOptions);
5198         }
5199     },
5200
5201     /**
5202      * Calls the specified function for each of the Records in the cache.
5203      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5204      * Returning <em>false</em> aborts and exits the iteration.
5205      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5206      */
5207     each : function(fn, scope){
5208         this.data.each(fn, scope);
5209     },
5210
5211     /**
5212      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5213      * (e.g., during paging).
5214      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5215      */
5216     getModifiedRecords : function(){
5217         return this.modified;
5218     },
5219
5220     // private
5221     createFilterFn : function(property, value, anyMatch){
5222         if(!value.exec){ // not a regex
5223             value = String(value);
5224             if(value.length == 0){
5225                 return false;
5226             }
5227             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5228         }
5229         return function(r){
5230             return value.test(r.data[property]);
5231         };
5232     },
5233
5234     /**
5235      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5236      * @param {String} property A field on your records
5237      * @param {Number} start The record index to start at (defaults to 0)
5238      * @param {Number} end The last record index to include (defaults to length - 1)
5239      * @return {Number} The sum
5240      */
5241     sum : function(property, start, end){
5242         var rs = this.data.items, v = 0;
5243         start = start || 0;
5244         end = (end || end === 0) ? end : rs.length-1;
5245
5246         for(var i = start; i <= end; i++){
5247             v += (rs[i].data[property] || 0);
5248         }
5249         return v;
5250     },
5251
5252     /**
5253      * Filter the records by a specified property.
5254      * @param {String} field A field on your records
5255      * @param {String/RegExp} value Either a string that the field
5256      * should start with or a RegExp to test against the field
5257      * @param {Boolean} anyMatch True to match any part not just the beginning
5258      */
5259     filter : function(property, value, anyMatch){
5260         var fn = this.createFilterFn(property, value, anyMatch);
5261         return fn ? this.filterBy(fn) : this.clearFilter();
5262     },
5263
5264     /**
5265      * Filter by a function. The specified function will be called with each
5266      * record in this data source. If the function returns true the record is included,
5267      * otherwise it is filtered.
5268      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5269      * @param {Object} scope (optional) The scope of the function (defaults to this)
5270      */
5271     filterBy : function(fn, scope){
5272         this.snapshot = this.snapshot || this.data;
5273         this.data = this.queryBy(fn, scope||this);
5274         this.fireEvent("datachanged", this);
5275     },
5276
5277     /**
5278      * Query the records by a specified property.
5279      * @param {String} field A field on your records
5280      * @param {String/RegExp} value Either a string that the field
5281      * should start with or a RegExp to test against the field
5282      * @param {Boolean} anyMatch True to match any part not just the beginning
5283      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5284      */
5285     query : function(property, value, anyMatch){
5286         var fn = this.createFilterFn(property, value, anyMatch);
5287         return fn ? this.queryBy(fn) : this.data.clone();
5288     },
5289
5290     /**
5291      * Query by a function. The specified function will be called with each
5292      * record in this data source. If the function returns true the record is included
5293      * in the results.
5294      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5295      * @param {Object} scope (optional) The scope of the function (defaults to this)
5296       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5297      **/
5298     queryBy : function(fn, scope){
5299         var data = this.snapshot || this.data;
5300         return data.filterBy(fn, scope||this);
5301     },
5302
5303     /**
5304      * Collects unique values for a particular dataIndex from this store.
5305      * @param {String} dataIndex The property to collect
5306      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5307      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5308      * @return {Array} An array of the unique values
5309      **/
5310     collect : function(dataIndex, allowNull, bypassFilter){
5311         var d = (bypassFilter === true && this.snapshot) ?
5312                 this.snapshot.items : this.data.items;
5313         var v, sv, r = [], l = {};
5314         for(var i = 0, len = d.length; i < len; i++){
5315             v = d[i].data[dataIndex];
5316             sv = String(v);
5317             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5318                 l[sv] = true;
5319                 r[r.length] = v;
5320             }
5321         }
5322         return r;
5323     },
5324
5325     /**
5326      * Revert to a view of the Record cache with no filtering applied.
5327      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5328      */
5329     clearFilter : function(suppressEvent){
5330         if(this.snapshot && this.snapshot != this.data){
5331             this.data = this.snapshot;
5332             delete this.snapshot;
5333             if(suppressEvent !== true){
5334                 this.fireEvent("datachanged", this);
5335             }
5336         }
5337     },
5338
5339     // private
5340     afterEdit : function(record){
5341         if(this.modified.indexOf(record) == -1){
5342             this.modified.push(record);
5343         }
5344         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5345     },
5346     
5347     // private
5348     afterReject : function(record){
5349         this.modified.remove(record);
5350         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5351     },
5352
5353     // private
5354     afterCommit : function(record){
5355         this.modified.remove(record);
5356         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5357     },
5358
5359     /**
5360      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5361      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5362      */
5363     commitChanges : function(){
5364         var m = this.modified.slice(0);
5365         this.modified = [];
5366         for(var i = 0, len = m.length; i < len; i++){
5367             m[i].commit();
5368         }
5369     },
5370
5371     /**
5372      * Cancel outstanding changes on all changed records.
5373      */
5374     rejectChanges : function(){
5375         var m = this.modified.slice(0);
5376         this.modified = [];
5377         for(var i = 0, len = m.length; i < len; i++){
5378             m[i].reject();
5379         }
5380     },
5381
5382     onMetaChange : function(meta, rtype, o){
5383         this.recordType = rtype;
5384         this.fields = rtype.prototype.fields;
5385         delete this.snapshot;
5386         this.sortInfo = meta.sortInfo || this.sortInfo;
5387         this.modified = [];
5388         this.fireEvent('metachange', this, this.reader.meta);
5389     }
5390 });/*
5391  * Based on:
5392  * Ext JS Library 1.1.1
5393  * Copyright(c) 2006-2007, Ext JS, LLC.
5394  *
5395  * Originally Released Under LGPL - original licence link has changed is not relivant.
5396  *
5397  * Fork - LGPL
5398  * <script type="text/javascript">
5399  */
5400
5401 /**
5402  * @class Roo.data.SimpleStore
5403  * @extends Roo.data.Store
5404  * Small helper class to make creating Stores from Array data easier.
5405  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5406  * @cfg {Array} fields An array of field definition objects, or field name strings.
5407  * @cfg {Array} data The multi-dimensional array of data
5408  * @constructor
5409  * @param {Object} config
5410  */
5411 Roo.data.SimpleStore = function(config){
5412     Roo.data.SimpleStore.superclass.constructor.call(this, {
5413         isLocal : true,
5414         reader: new Roo.data.ArrayReader({
5415                 id: config.id
5416             },
5417             Roo.data.Record.create(config.fields)
5418         ),
5419         proxy : new Roo.data.MemoryProxy(config.data)
5420     });
5421     this.load();
5422 };
5423 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5424  * Based on:
5425  * Ext JS Library 1.1.1
5426  * Copyright(c) 2006-2007, Ext JS, LLC.
5427  *
5428  * Originally Released Under LGPL - original licence link has changed is not relivant.
5429  *
5430  * Fork - LGPL
5431  * <script type="text/javascript">
5432  */
5433
5434 /**
5435 /**
5436  * @extends Roo.data.Store
5437  * @class Roo.data.JsonStore
5438  * Small helper class to make creating Stores for JSON data easier. <br/>
5439 <pre><code>
5440 var store = new Roo.data.JsonStore({
5441     url: 'get-images.php',
5442     root: 'images',
5443     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5444 });
5445 </code></pre>
5446  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5447  * JsonReader and HttpProxy (unless inline data is provided).</b>
5448  * @cfg {Array} fields An array of field definition objects, or field name strings.
5449  * @constructor
5450  * @param {Object} config
5451  */
5452 Roo.data.JsonStore = function(c){
5453     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5454         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5455         reader: new Roo.data.JsonReader(c, c.fields)
5456     }));
5457 };
5458 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5459  * Based on:
5460  * Ext JS Library 1.1.1
5461  * Copyright(c) 2006-2007, Ext JS, LLC.
5462  *
5463  * Originally Released Under LGPL - original licence link has changed is not relivant.
5464  *
5465  * Fork - LGPL
5466  * <script type="text/javascript">
5467  */
5468
5469  
5470 Roo.data.Field = function(config){
5471     if(typeof config == "string"){
5472         config = {name: config};
5473     }
5474     Roo.apply(this, config);
5475     
5476     if(!this.type){
5477         this.type = "auto";
5478     }
5479     
5480     var st = Roo.data.SortTypes;
5481     // named sortTypes are supported, here we look them up
5482     if(typeof this.sortType == "string"){
5483         this.sortType = st[this.sortType];
5484     }
5485     
5486     // set default sortType for strings and dates
5487     if(!this.sortType){
5488         switch(this.type){
5489             case "string":
5490                 this.sortType = st.asUCString;
5491                 break;
5492             case "date":
5493                 this.sortType = st.asDate;
5494                 break;
5495             default:
5496                 this.sortType = st.none;
5497         }
5498     }
5499
5500     // define once
5501     var stripRe = /[\$,%]/g;
5502
5503     // prebuilt conversion function for this field, instead of
5504     // switching every time we're reading a value
5505     if(!this.convert){
5506         var cv, dateFormat = this.dateFormat;
5507         switch(this.type){
5508             case "":
5509             case "auto":
5510             case undefined:
5511                 cv = function(v){ return v; };
5512                 break;
5513             case "string":
5514                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5515                 break;
5516             case "int":
5517                 cv = function(v){
5518                     return v !== undefined && v !== null && v !== '' ?
5519                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5520                     };
5521                 break;
5522             case "float":
5523                 cv = function(v){
5524                     return v !== undefined && v !== null && v !== '' ?
5525                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5526                     };
5527                 break;
5528             case "bool":
5529             case "boolean":
5530                 cv = function(v){ return v === true || v === "true" || v == 1; };
5531                 break;
5532             case "date":
5533                 cv = function(v){
5534                     if(!v){
5535                         return '';
5536                     }
5537                     if(v instanceof Date){
5538                         return v;
5539                     }
5540                     if(dateFormat){
5541                         if(dateFormat == "timestamp"){
5542                             return new Date(v*1000);
5543                         }
5544                         return Date.parseDate(v, dateFormat);
5545                     }
5546                     var parsed = Date.parse(v);
5547                     return parsed ? new Date(parsed) : null;
5548                 };
5549              break;
5550             
5551         }
5552         this.convert = cv;
5553     }
5554 };
5555
5556 Roo.data.Field.prototype = {
5557     dateFormat: null,
5558     defaultValue: "",
5559     mapping: null,
5560     sortType : null,
5561     sortDir : "ASC"
5562 };/*
5563  * Based on:
5564  * Ext JS Library 1.1.1
5565  * Copyright(c) 2006-2007, Ext JS, LLC.
5566  *
5567  * Originally Released Under LGPL - original licence link has changed is not relivant.
5568  *
5569  * Fork - LGPL
5570  * <script type="text/javascript">
5571  */
5572  
5573 // Base class for reading structured data from a data source.  This class is intended to be
5574 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5575
5576 /**
5577  * @class Roo.data.DataReader
5578  * Base class for reading structured data from a data source.  This class is intended to be
5579  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5580  */
5581
5582 Roo.data.DataReader = function(meta, recordType){
5583     
5584     this.meta = meta;
5585     
5586     this.recordType = recordType instanceof Array ? 
5587         Roo.data.Record.create(recordType) : recordType;
5588 };
5589
5590 Roo.data.DataReader.prototype = {
5591      /**
5592      * Create an empty record
5593      * @param {Object} data (optional) - overlay some values
5594      * @return {Roo.data.Record} record created.
5595      */
5596     newRow :  function(d) {
5597         var da =  {};
5598         this.recordType.prototype.fields.each(function(c) {
5599             switch( c.type) {
5600                 case 'int' : da[c.name] = 0; break;
5601                 case 'date' : da[c.name] = new Date(); break;
5602                 case 'float' : da[c.name] = 0.0; break;
5603                 case 'boolean' : da[c.name] = false; break;
5604                 default : da[c.name] = ""; break;
5605             }
5606             
5607         });
5608         return new this.recordType(Roo.apply(da, d));
5609     }
5610     
5611 };/*
5612  * Based on:
5613  * Ext JS Library 1.1.1
5614  * Copyright(c) 2006-2007, Ext JS, LLC.
5615  *
5616  * Originally Released Under LGPL - original licence link has changed is not relivant.
5617  *
5618  * Fork - LGPL
5619  * <script type="text/javascript">
5620  */
5621
5622 /**
5623  * @class Roo.data.DataProxy
5624  * @extends Roo.data.Observable
5625  * This class is an abstract base class for implementations which provide retrieval of
5626  * unformatted data objects.<br>
5627  * <p>
5628  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5629  * (of the appropriate type which knows how to parse the data object) to provide a block of
5630  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5631  * <p>
5632  * Custom implementations must implement the load method as described in
5633  * {@link Roo.data.HttpProxy#load}.
5634  */
5635 Roo.data.DataProxy = function(){
5636     this.addEvents({
5637         /**
5638          * @event beforeload
5639          * Fires before a network request is made to retrieve a data object.
5640          * @param {Object} This DataProxy object.
5641          * @param {Object} params The params parameter to the load function.
5642          */
5643         beforeload : true,
5644         /**
5645          * @event load
5646          * Fires before the load method's callback is called.
5647          * @param {Object} This DataProxy object.
5648          * @param {Object} o The data object.
5649          * @param {Object} arg The callback argument object passed to the load function.
5650          */
5651         load : true,
5652         /**
5653          * @event loadexception
5654          * Fires if an Exception occurs during data retrieval.
5655          * @param {Object} This DataProxy object.
5656          * @param {Object} o The data object.
5657          * @param {Object} arg The callback argument object passed to the load function.
5658          * @param {Object} e The Exception.
5659          */
5660         loadexception : true
5661     });
5662     Roo.data.DataProxy.superclass.constructor.call(this);
5663 };
5664
5665 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5666
5667     /**
5668      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5669      */
5670 /*
5671  * Based on:
5672  * Ext JS Library 1.1.1
5673  * Copyright(c) 2006-2007, Ext JS, LLC.
5674  *
5675  * Originally Released Under LGPL - original licence link has changed is not relivant.
5676  *
5677  * Fork - LGPL
5678  * <script type="text/javascript">
5679  */
5680 /**
5681  * @class Roo.data.MemoryProxy
5682  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5683  * to the Reader when its load method is called.
5684  * @constructor
5685  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5686  */
5687 Roo.data.MemoryProxy = function(data){
5688     if (data.data) {
5689         data = data.data;
5690     }
5691     Roo.data.MemoryProxy.superclass.constructor.call(this);
5692     this.data = data;
5693 };
5694
5695 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5696     /**
5697      * Load data from the requested source (in this case an in-memory
5698      * data object passed to the constructor), read the data object into
5699      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5700      * process that block using the passed callback.
5701      * @param {Object} params This parameter is not used by the MemoryProxy class.
5702      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5703      * object into a block of Roo.data.Records.
5704      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5705      * The function must be passed <ul>
5706      * <li>The Record block object</li>
5707      * <li>The "arg" argument from the load function</li>
5708      * <li>A boolean success indicator</li>
5709      * </ul>
5710      * @param {Object} scope The scope in which to call the callback
5711      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5712      */
5713     load : function(params, reader, callback, scope, arg){
5714         params = params || {};
5715         var result;
5716         try {
5717             result = reader.readRecords(this.data);
5718         }catch(e){
5719             this.fireEvent("loadexception", this, arg, null, e);
5720             callback.call(scope, null, arg, false);
5721             return;
5722         }
5723         callback.call(scope, result, arg, true);
5724     },
5725     
5726     // private
5727     update : function(params, records){
5728         
5729     }
5730 });/*
5731  * Based on:
5732  * Ext JS Library 1.1.1
5733  * Copyright(c) 2006-2007, Ext JS, LLC.
5734  *
5735  * Originally Released Under LGPL - original licence link has changed is not relivant.
5736  *
5737  * Fork - LGPL
5738  * <script type="text/javascript">
5739  */
5740 /**
5741  * @class Roo.data.HttpProxy
5742  * @extends Roo.data.DataProxy
5743  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5744  * configured to reference a certain URL.<br><br>
5745  * <p>
5746  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5747  * from which the running page was served.<br><br>
5748  * <p>
5749  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5750  * <p>
5751  * Be aware that to enable the browser to parse an XML document, the server must set
5752  * the Content-Type header in the HTTP response to "text/xml".
5753  * @constructor
5754  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5755  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5756  * will be used to make the request.
5757  */
5758 Roo.data.HttpProxy = function(conn){
5759     Roo.data.HttpProxy.superclass.constructor.call(this);
5760     // is conn a conn config or a real conn?
5761     this.conn = conn;
5762     this.useAjax = !conn || !conn.events;
5763   
5764 };
5765
5766 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5767     // thse are take from connection...
5768     
5769     /**
5770      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5771      */
5772     /**
5773      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5774      * extra parameters to each request made by this object. (defaults to undefined)
5775      */
5776     /**
5777      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5778      *  to each request made by this object. (defaults to undefined)
5779      */
5780     /**
5781      * @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)
5782      */
5783     /**
5784      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5785      */
5786      /**
5787      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5788      * @type Boolean
5789      */
5790   
5791
5792     /**
5793      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5794      * @type Boolean
5795      */
5796     /**
5797      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5798      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5799      * a finer-grained basis than the DataProxy events.
5800      */
5801     getConnection : function(){
5802         return this.useAjax ? Roo.Ajax : this.conn;
5803     },
5804
5805     /**
5806      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5807      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5808      * process that block using the passed callback.
5809      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5810      * for the request to the remote server.
5811      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5812      * object into a block of Roo.data.Records.
5813      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5814      * The function must be passed <ul>
5815      * <li>The Record block object</li>
5816      * <li>The "arg" argument from the load function</li>
5817      * <li>A boolean success indicator</li>
5818      * </ul>
5819      * @param {Object} scope The scope in which to call the callback
5820      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5821      */
5822     load : function(params, reader, callback, scope, arg){
5823         if(this.fireEvent("beforeload", this, params) !== false){
5824             var  o = {
5825                 params : params || {},
5826                 request: {
5827                     callback : callback,
5828                     scope : scope,
5829                     arg : arg
5830                 },
5831                 reader: reader,
5832                 callback : this.loadResponse,
5833                 scope: this
5834             };
5835             if(this.useAjax){
5836                 Roo.applyIf(o, this.conn);
5837                 if(this.activeRequest){
5838                     Roo.Ajax.abort(this.activeRequest);
5839                 }
5840                 this.activeRequest = Roo.Ajax.request(o);
5841             }else{
5842                 this.conn.request(o);
5843             }
5844         }else{
5845             callback.call(scope||this, null, arg, false);
5846         }
5847     },
5848
5849     // private
5850     loadResponse : function(o, success, response){
5851         delete this.activeRequest;
5852         if(!success){
5853             this.fireEvent("loadexception", this, o, response);
5854             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5855             return;
5856         }
5857         var result;
5858         try {
5859             result = o.reader.read(response);
5860         }catch(e){
5861             this.fireEvent("loadexception", this, o, response, e);
5862             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5863             return;
5864         }
5865         
5866         this.fireEvent("load", this, o, o.request.arg);
5867         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5868     },
5869
5870     // private
5871     update : function(dataSet){
5872
5873     },
5874
5875     // private
5876     updateResponse : function(dataSet){
5877
5878     }
5879 });/*
5880  * Based on:
5881  * Ext JS Library 1.1.1
5882  * Copyright(c) 2006-2007, Ext JS, LLC.
5883  *
5884  * Originally Released Under LGPL - original licence link has changed is not relivant.
5885  *
5886  * Fork - LGPL
5887  * <script type="text/javascript">
5888  */
5889
5890 /**
5891  * @class Roo.data.ScriptTagProxy
5892  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5893  * other than the originating domain of the running page.<br><br>
5894  * <p>
5895  * <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
5896  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5897  * <p>
5898  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5899  * source code that is used as the source inside a &lt;script> tag.<br><br>
5900  * <p>
5901  * In order for the browser to process the returned data, the server must wrap the data object
5902  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5903  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5904  * depending on whether the callback name was passed:
5905  * <p>
5906  * <pre><code>
5907 boolean scriptTag = false;
5908 String cb = request.getParameter("callback");
5909 if (cb != null) {
5910     scriptTag = true;
5911     response.setContentType("text/javascript");
5912 } else {
5913     response.setContentType("application/x-json");
5914 }
5915 Writer out = response.getWriter();
5916 if (scriptTag) {
5917     out.write(cb + "(");
5918 }
5919 out.print(dataBlock.toJsonString());
5920 if (scriptTag) {
5921     out.write(");");
5922 }
5923 </pre></code>
5924  *
5925  * @constructor
5926  * @param {Object} config A configuration object.
5927  */
5928 Roo.data.ScriptTagProxy = function(config){
5929     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5930     Roo.apply(this, config);
5931     this.head = document.getElementsByTagName("head")[0];
5932 };
5933
5934 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5935
5936 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5937     /**
5938      * @cfg {String} url The URL from which to request the data object.
5939      */
5940     /**
5941      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5942      */
5943     timeout : 30000,
5944     /**
5945      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5946      * the server the name of the callback function set up by the load call to process the returned data object.
5947      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5948      * javascript output which calls this named function passing the data object as its only parameter.
5949      */
5950     callbackParam : "callback",
5951     /**
5952      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5953      * name to the request.
5954      */
5955     nocache : true,
5956
5957     /**
5958      * Load data from the configured URL, read the data object into
5959      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5960      * process that block using the passed callback.
5961      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5962      * for the request to the remote server.
5963      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5964      * object into a block of Roo.data.Records.
5965      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5966      * The function must be passed <ul>
5967      * <li>The Record block object</li>
5968      * <li>The "arg" argument from the load function</li>
5969      * <li>A boolean success indicator</li>
5970      * </ul>
5971      * @param {Object} scope The scope in which to call the callback
5972      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5973      */
5974     load : function(params, reader, callback, scope, arg){
5975         if(this.fireEvent("beforeload", this, params) !== false){
5976
5977             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5978
5979             var url = this.url;
5980             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5981             if(this.nocache){
5982                 url += "&_dc=" + (new Date().getTime());
5983             }
5984             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5985             var trans = {
5986                 id : transId,
5987                 cb : "stcCallback"+transId,
5988                 scriptId : "stcScript"+transId,
5989                 params : params,
5990                 arg : arg,
5991                 url : url,
5992                 callback : callback,
5993                 scope : scope,
5994                 reader : reader
5995             };
5996             var conn = this;
5997
5998             window[trans.cb] = function(o){
5999                 conn.handleResponse(o, trans);
6000             };
6001
6002             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6003
6004             if(this.autoAbort !== false){
6005                 this.abort();
6006             }
6007
6008             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6009
6010             var script = document.createElement("script");
6011             script.setAttribute("src", url);
6012             script.setAttribute("type", "text/javascript");
6013             script.setAttribute("id", trans.scriptId);
6014             this.head.appendChild(script);
6015
6016             this.trans = trans;
6017         }else{
6018             callback.call(scope||this, null, arg, false);
6019         }
6020     },
6021
6022     // private
6023     isLoading : function(){
6024         return this.trans ? true : false;
6025     },
6026
6027     /**
6028      * Abort the current server request.
6029      */
6030     abort : function(){
6031         if(this.isLoading()){
6032             this.destroyTrans(this.trans);
6033         }
6034     },
6035
6036     // private
6037     destroyTrans : function(trans, isLoaded){
6038         this.head.removeChild(document.getElementById(trans.scriptId));
6039         clearTimeout(trans.timeoutId);
6040         if(isLoaded){
6041             window[trans.cb] = undefined;
6042             try{
6043                 delete window[trans.cb];
6044             }catch(e){}
6045         }else{
6046             // if hasn't been loaded, wait for load to remove it to prevent script error
6047             window[trans.cb] = function(){
6048                 window[trans.cb] = undefined;
6049                 try{
6050                     delete window[trans.cb];
6051                 }catch(e){}
6052             };
6053         }
6054     },
6055
6056     // private
6057     handleResponse : function(o, trans){
6058         this.trans = false;
6059         this.destroyTrans(trans, true);
6060         var result;
6061         try {
6062             result = trans.reader.readRecords(o);
6063         }catch(e){
6064             this.fireEvent("loadexception", this, o, trans.arg, e);
6065             trans.callback.call(trans.scope||window, null, trans.arg, false);
6066             return;
6067         }
6068         this.fireEvent("load", this, o, trans.arg);
6069         trans.callback.call(trans.scope||window, result, trans.arg, true);
6070     },
6071
6072     // private
6073     handleFailure : function(trans){
6074         this.trans = false;
6075         this.destroyTrans(trans, false);
6076         this.fireEvent("loadexception", this, null, trans.arg);
6077         trans.callback.call(trans.scope||window, null, trans.arg, false);
6078     }
6079 });/*
6080  * Based on:
6081  * Ext JS Library 1.1.1
6082  * Copyright(c) 2006-2007, Ext JS, LLC.
6083  *
6084  * Originally Released Under LGPL - original licence link has changed is not relivant.
6085  *
6086  * Fork - LGPL
6087  * <script type="text/javascript">
6088  */
6089
6090 /**
6091  * @class Roo.data.JsonReader
6092  * @extends Roo.data.DataReader
6093  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6094  * based on mappings in a provided Roo.data.Record constructor.
6095  * 
6096  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6097  * in the reply previously. 
6098  * 
6099  * <p>
6100  * Example code:
6101  * <pre><code>
6102 var RecordDef = Roo.data.Record.create([
6103     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6104     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6105 ]);
6106 var myReader = new Roo.data.JsonReader({
6107     totalProperty: "results",    // The property which contains the total dataset size (optional)
6108     root: "rows",                // The property which contains an Array of row objects
6109     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6110 }, RecordDef);
6111 </code></pre>
6112  * <p>
6113  * This would consume a JSON file like this:
6114  * <pre><code>
6115 { 'results': 2, 'rows': [
6116     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6117     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6118 }
6119 </code></pre>
6120  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6121  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6122  * paged from the remote server.
6123  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6124  * @cfg {String} root name of the property which contains the Array of row objects.
6125  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6126  * @constructor
6127  * Create a new JsonReader
6128  * @param {Object} meta Metadata configuration options
6129  * @param {Object} recordType Either an Array of field definition objects,
6130  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6131  */
6132 Roo.data.JsonReader = function(meta, recordType){
6133     
6134     meta = meta || {};
6135     // set some defaults:
6136     Roo.applyIf(meta, {
6137         totalProperty: 'total',
6138         successProperty : 'success',
6139         root : 'data',
6140         id : 'id'
6141     });
6142     
6143     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6144 };
6145 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6146     
6147     /**
6148      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6149      * Used by Store query builder to append _requestMeta to params.
6150      * 
6151      */
6152     metaFromRemote : false,
6153     /**
6154      * This method is only used by a DataProxy which has retrieved data from a remote server.
6155      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6156      * @return {Object} data A data block which is used by an Roo.data.Store object as
6157      * a cache of Roo.data.Records.
6158      */
6159     read : function(response){
6160         var json = response.responseText;
6161        
6162         var o = /* eval:var:o */ eval("("+json+")");
6163         if(!o) {
6164             throw {message: "JsonReader.read: Json object not found"};
6165         }
6166         
6167         if(o.metaData){
6168             
6169             delete this.ef;
6170             this.metaFromRemote = true;
6171             this.meta = o.metaData;
6172             this.recordType = Roo.data.Record.create(o.metaData.fields);
6173             this.onMetaChange(this.meta, this.recordType, o);
6174         }
6175         return this.readRecords(o);
6176     },
6177
6178     // private function a store will implement
6179     onMetaChange : function(meta, recordType, o){
6180
6181     },
6182
6183     /**
6184          * @ignore
6185          */
6186     simpleAccess: function(obj, subsc) {
6187         return obj[subsc];
6188     },
6189
6190         /**
6191          * @ignore
6192          */
6193     getJsonAccessor: function(){
6194         var re = /[\[\.]/;
6195         return function(expr) {
6196             try {
6197                 return(re.test(expr))
6198                     ? new Function("obj", "return obj." + expr)
6199                     : function(obj){
6200                         return obj[expr];
6201                     };
6202             } catch(e){}
6203             return Roo.emptyFn;
6204         };
6205     }(),
6206
6207     /**
6208      * Create a data block containing Roo.data.Records from an XML document.
6209      * @param {Object} o An object which contains an Array of row objects in the property specified
6210      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6211      * which contains the total size of the dataset.
6212      * @return {Object} data A data block which is used by an Roo.data.Store object as
6213      * a cache of Roo.data.Records.
6214      */
6215     readRecords : function(o){
6216         /**
6217          * After any data loads, the raw JSON data is available for further custom processing.
6218          * @type Object
6219          */
6220         this.jsonData = o;
6221         var s = this.meta, Record = this.recordType,
6222             f = Record.prototype.fields, fi = f.items, fl = f.length;
6223
6224 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6225         if (!this.ef) {
6226             if(s.totalProperty) {
6227                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6228                 }
6229                 if(s.successProperty) {
6230                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6231                 }
6232                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6233                 if (s.id) {
6234                         var g = this.getJsonAccessor(s.id);
6235                         this.getId = function(rec) {
6236                                 var r = g(rec);
6237                                 return (r === undefined || r === "") ? null : r;
6238                         };
6239                 } else {
6240                         this.getId = function(){return null;};
6241                 }
6242             this.ef = [];
6243             for(var jj = 0; jj < fl; jj++){
6244                 f = fi[jj];
6245                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6246                 this.ef[jj] = this.getJsonAccessor(map);
6247             }
6248         }
6249
6250         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6251         if(s.totalProperty){
6252             var vt = parseInt(this.getTotal(o), 10);
6253             if(!isNaN(vt)){
6254                 totalRecords = vt;
6255             }
6256         }
6257         if(s.successProperty){
6258             var vs = this.getSuccess(o);
6259             if(vs === false || vs === 'false'){
6260                 success = false;
6261             }
6262         }
6263         var records = [];
6264             for(var i = 0; i < c; i++){
6265                     var n = root[i];
6266                 var values = {};
6267                 var id = this.getId(n);
6268                 for(var j = 0; j < fl; j++){
6269                     f = fi[j];
6270                 var v = this.ef[j](n);
6271                 if (!f.convert) {
6272                     Roo.log('missing convert for ' + f.name);
6273                     Roo.log(f);
6274                     continue;
6275                 }
6276                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6277                 }
6278                 var record = new Record(values, id);
6279                 record.json = n;
6280                 records[i] = record;
6281             }
6282             return {
6283                 success : success,
6284                 records : records,
6285                 totalRecords : totalRecords
6286             };
6287     }
6288 });/*
6289  * Based on:
6290  * Ext JS Library 1.1.1
6291  * Copyright(c) 2006-2007, Ext JS, LLC.
6292  *
6293  * Originally Released Under LGPL - original licence link has changed is not relivant.
6294  *
6295  * Fork - LGPL
6296  * <script type="text/javascript">
6297  */
6298
6299 /**
6300  * @class Roo.data.XmlReader
6301  * @extends Roo.data.DataReader
6302  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6303  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6304  * <p>
6305  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6306  * header in the HTTP response must be set to "text/xml".</em>
6307  * <p>
6308  * Example code:
6309  * <pre><code>
6310 var RecordDef = Roo.data.Record.create([
6311    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6312    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6313 ]);
6314 var myReader = new Roo.data.XmlReader({
6315    totalRecords: "results", // The element which contains the total dataset size (optional)
6316    record: "row",           // The repeated element which contains row information
6317    id: "id"                 // The element within the row that provides an ID for the record (optional)
6318 }, RecordDef);
6319 </code></pre>
6320  * <p>
6321  * This would consume an XML file like this:
6322  * <pre><code>
6323 &lt;?xml?>
6324 &lt;dataset>
6325  &lt;results>2&lt;/results>
6326  &lt;row>
6327    &lt;id>1&lt;/id>
6328    &lt;name>Bill&lt;/name>
6329    &lt;occupation>Gardener&lt;/occupation>
6330  &lt;/row>
6331  &lt;row>
6332    &lt;id>2&lt;/id>
6333    &lt;name>Ben&lt;/name>
6334    &lt;occupation>Horticulturalist&lt;/occupation>
6335  &lt;/row>
6336 &lt;/dataset>
6337 </code></pre>
6338  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6339  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6340  * paged from the remote server.
6341  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6342  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6343  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6344  * a record identifier value.
6345  * @constructor
6346  * Create a new XmlReader
6347  * @param {Object} meta Metadata configuration options
6348  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6349  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6350  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6351  */
6352 Roo.data.XmlReader = function(meta, recordType){
6353     meta = meta || {};
6354     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6355 };
6356 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6357     /**
6358      * This method is only used by a DataProxy which has retrieved data from a remote server.
6359          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6360          * to contain a method called 'responseXML' that returns an XML document object.
6361      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6362      * a cache of Roo.data.Records.
6363      */
6364     read : function(response){
6365         var doc = response.responseXML;
6366         if(!doc) {
6367             throw {message: "XmlReader.read: XML Document not available"};
6368         }
6369         return this.readRecords(doc);
6370     },
6371
6372     /**
6373      * Create a data block containing Roo.data.Records from an XML document.
6374          * @param {Object} doc A parsed XML document.
6375      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6376      * a cache of Roo.data.Records.
6377      */
6378     readRecords : function(doc){
6379         /**
6380          * After any data loads/reads, the raw XML Document is available for further custom processing.
6381          * @type XMLDocument
6382          */
6383         this.xmlData = doc;
6384         var root = doc.documentElement || doc;
6385         var q = Roo.DomQuery;
6386         var recordType = this.recordType, fields = recordType.prototype.fields;
6387         var sid = this.meta.id;
6388         var totalRecords = 0, success = true;
6389         if(this.meta.totalRecords){
6390             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6391         }
6392         
6393         if(this.meta.success){
6394             var sv = q.selectValue(this.meta.success, root, true);
6395             success = sv !== false && sv !== 'false';
6396         }
6397         var records = [];
6398         var ns = q.select(this.meta.record, root);
6399         for(var i = 0, len = ns.length; i < len; i++) {
6400                 var n = ns[i];
6401                 var values = {};
6402                 var id = sid ? q.selectValue(sid, n) : undefined;
6403                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6404                     var f = fields.items[j];
6405                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6406                     v = f.convert(v);
6407                     values[f.name] = v;
6408                 }
6409                 var record = new recordType(values, id);
6410                 record.node = n;
6411                 records[records.length] = record;
6412             }
6413
6414             return {
6415                 success : success,
6416                 records : records,
6417                 totalRecords : totalRecords || records.length
6418             };
6419     }
6420 });/*
6421  * Based on:
6422  * Ext JS Library 1.1.1
6423  * Copyright(c) 2006-2007, Ext JS, LLC.
6424  *
6425  * Originally Released Under LGPL - original licence link has changed is not relivant.
6426  *
6427  * Fork - LGPL
6428  * <script type="text/javascript">
6429  */
6430
6431 /**
6432  * @class Roo.data.ArrayReader
6433  * @extends Roo.data.DataReader
6434  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6435  * Each element of that Array represents a row of data fields. The
6436  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6437  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6438  * <p>
6439  * Example code:.
6440  * <pre><code>
6441 var RecordDef = Roo.data.Record.create([
6442     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6443     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6444 ]);
6445 var myReader = new Roo.data.ArrayReader({
6446     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6447 }, RecordDef);
6448 </code></pre>
6449  * <p>
6450  * This would consume an Array like this:
6451  * <pre><code>
6452 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6453   </code></pre>
6454  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6455  * @constructor
6456  * Create a new JsonReader
6457  * @param {Object} meta Metadata configuration options.
6458  * @param {Object} recordType Either an Array of field definition objects
6459  * as specified to {@link Roo.data.Record#create},
6460  * or an {@link Roo.data.Record} object
6461  * created using {@link Roo.data.Record#create}.
6462  */
6463 Roo.data.ArrayReader = function(meta, recordType){
6464     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6465 };
6466
6467 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6468     /**
6469      * Create a data block containing Roo.data.Records from an XML document.
6470      * @param {Object} o An Array of row objects which represents the dataset.
6471      * @return {Object} data A data block which is used by an Roo.data.Store object as
6472      * a cache of Roo.data.Records.
6473      */
6474     readRecords : function(o){
6475         var sid = this.meta ? this.meta.id : null;
6476         var recordType = this.recordType, fields = recordType.prototype.fields;
6477         var records = [];
6478         var root = o;
6479             for(var i = 0; i < root.length; i++){
6480                     var n = root[i];
6481                 var values = {};
6482                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6483                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6484                 var f = fields.items[j];
6485                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6486                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6487                 v = f.convert(v);
6488                 values[f.name] = v;
6489             }
6490                 var record = new recordType(values, id);
6491                 record.json = n;
6492                 records[records.length] = record;
6493             }
6494             return {
6495                 records : records,
6496                 totalRecords : records.length
6497             };
6498     }
6499 });/*
6500  * Based on:
6501  * Ext JS Library 1.1.1
6502  * Copyright(c) 2006-2007, Ext JS, LLC.
6503  *
6504  * Originally Released Under LGPL - original licence link has changed is not relivant.
6505  *
6506  * Fork - LGPL
6507  * <script type="text/javascript">
6508  */
6509
6510
6511 /**
6512  * @class Roo.data.Tree
6513  * @extends Roo.util.Observable
6514  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6515  * in the tree have most standard DOM functionality.
6516  * @constructor
6517  * @param {Node} root (optional) The root node
6518  */
6519 Roo.data.Tree = function(root){
6520    this.nodeHash = {};
6521    /**
6522     * The root node for this tree
6523     * @type Node
6524     */
6525    this.root = null;
6526    if(root){
6527        this.setRootNode(root);
6528    }
6529    this.addEvents({
6530        /**
6531         * @event append
6532         * Fires when a new child node is appended to a node in this tree.
6533         * @param {Tree} tree The owner tree
6534         * @param {Node} parent The parent node
6535         * @param {Node} node The newly appended node
6536         * @param {Number} index The index of the newly appended node
6537         */
6538        "append" : true,
6539        /**
6540         * @event remove
6541         * Fires when a child node is removed from a node in this tree.
6542         * @param {Tree} tree The owner tree
6543         * @param {Node} parent The parent node
6544         * @param {Node} node The child node removed
6545         */
6546        "remove" : true,
6547        /**
6548         * @event move
6549         * Fires when a node is moved to a new location in the tree
6550         * @param {Tree} tree The owner tree
6551         * @param {Node} node The node moved
6552         * @param {Node} oldParent The old parent of this node
6553         * @param {Node} newParent The new parent of this node
6554         * @param {Number} index The index it was moved to
6555         */
6556        "move" : true,
6557        /**
6558         * @event insert
6559         * Fires when a new child node is inserted in a node in this tree.
6560         * @param {Tree} tree The owner tree
6561         * @param {Node} parent The parent node
6562         * @param {Node} node The child node inserted
6563         * @param {Node} refNode The child node the node was inserted before
6564         */
6565        "insert" : true,
6566        /**
6567         * @event beforeappend
6568         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6569         * @param {Tree} tree The owner tree
6570         * @param {Node} parent The parent node
6571         * @param {Node} node The child node to be appended
6572         */
6573        "beforeappend" : true,
6574        /**
6575         * @event beforeremove
6576         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6577         * @param {Tree} tree The owner tree
6578         * @param {Node} parent The parent node
6579         * @param {Node} node The child node to be removed
6580         */
6581        "beforeremove" : true,
6582        /**
6583         * @event beforemove
6584         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6585         * @param {Tree} tree The owner tree
6586         * @param {Node} node The node being moved
6587         * @param {Node} oldParent The parent of the node
6588         * @param {Node} newParent The new parent the node is moving to
6589         * @param {Number} index The index it is being moved to
6590         */
6591        "beforemove" : true,
6592        /**
6593         * @event beforeinsert
6594         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6595         * @param {Tree} tree The owner tree
6596         * @param {Node} parent The parent node
6597         * @param {Node} node The child node to be inserted
6598         * @param {Node} refNode The child node the node is being inserted before
6599         */
6600        "beforeinsert" : true
6601    });
6602
6603     Roo.data.Tree.superclass.constructor.call(this);
6604 };
6605
6606 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6607     pathSeparator: "/",
6608
6609     proxyNodeEvent : function(){
6610         return this.fireEvent.apply(this, arguments);
6611     },
6612
6613     /**
6614      * Returns the root node for this tree.
6615      * @return {Node}
6616      */
6617     getRootNode : function(){
6618         return this.root;
6619     },
6620
6621     /**
6622      * Sets the root node for this tree.
6623      * @param {Node} node
6624      * @return {Node}
6625      */
6626     setRootNode : function(node){
6627         this.root = node;
6628         node.ownerTree = this;
6629         node.isRoot = true;
6630         this.registerNode(node);
6631         return node;
6632     },
6633
6634     /**
6635      * Gets a node in this tree by its id.
6636      * @param {String} id
6637      * @return {Node}
6638      */
6639     getNodeById : function(id){
6640         return this.nodeHash[id];
6641     },
6642
6643     registerNode : function(node){
6644         this.nodeHash[node.id] = node;
6645     },
6646
6647     unregisterNode : function(node){
6648         delete this.nodeHash[node.id];
6649     },
6650
6651     toString : function(){
6652         return "[Tree"+(this.id?" "+this.id:"")+"]";
6653     }
6654 });
6655
6656 /**
6657  * @class Roo.data.Node
6658  * @extends Roo.util.Observable
6659  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6660  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6661  * @constructor
6662  * @param {Object} attributes The attributes/config for the node
6663  */
6664 Roo.data.Node = function(attributes){
6665     /**
6666      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6667      * @type {Object}
6668      */
6669     this.attributes = attributes || {};
6670     this.leaf = this.attributes.leaf;
6671     /**
6672      * The node id. @type String
6673      */
6674     this.id = this.attributes.id;
6675     if(!this.id){
6676         this.id = Roo.id(null, "ynode-");
6677         this.attributes.id = this.id;
6678     }
6679      
6680     
6681     /**
6682      * All child nodes of this node. @type Array
6683      */
6684     this.childNodes = [];
6685     if(!this.childNodes.indexOf){ // indexOf is a must
6686         this.childNodes.indexOf = function(o){
6687             for(var i = 0, len = this.length; i < len; i++){
6688                 if(this[i] == o) {
6689                     return i;
6690                 }
6691             }
6692             return -1;
6693         };
6694     }
6695     /**
6696      * The parent node for this node. @type Node
6697      */
6698     this.parentNode = null;
6699     /**
6700      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6701      */
6702     this.firstChild = null;
6703     /**
6704      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6705      */
6706     this.lastChild = null;
6707     /**
6708      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6709      */
6710     this.previousSibling = null;
6711     /**
6712      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6713      */
6714     this.nextSibling = null;
6715
6716     this.addEvents({
6717        /**
6718         * @event append
6719         * Fires when a new child node is appended
6720         * @param {Tree} tree The owner tree
6721         * @param {Node} this This node
6722         * @param {Node} node The newly appended node
6723         * @param {Number} index The index of the newly appended node
6724         */
6725        "append" : true,
6726        /**
6727         * @event remove
6728         * Fires when a child node is removed
6729         * @param {Tree} tree The owner tree
6730         * @param {Node} this This node
6731         * @param {Node} node The removed node
6732         */
6733        "remove" : true,
6734        /**
6735         * @event move
6736         * Fires when this node is moved to a new location in the tree
6737         * @param {Tree} tree The owner tree
6738         * @param {Node} this This node
6739         * @param {Node} oldParent The old parent of this node
6740         * @param {Node} newParent The new parent of this node
6741         * @param {Number} index The index it was moved to
6742         */
6743        "move" : true,
6744        /**
6745         * @event insert
6746         * Fires when a new child node is inserted.
6747         * @param {Tree} tree The owner tree
6748         * @param {Node} this This node
6749         * @param {Node} node The child node inserted
6750         * @param {Node} refNode The child node the node was inserted before
6751         */
6752        "insert" : true,
6753        /**
6754         * @event beforeappend
6755         * Fires before a new child is appended, return false to cancel the append.
6756         * @param {Tree} tree The owner tree
6757         * @param {Node} this This node
6758         * @param {Node} node The child node to be appended
6759         */
6760        "beforeappend" : true,
6761        /**
6762         * @event beforeremove
6763         * Fires before a child is removed, return false to cancel the remove.
6764         * @param {Tree} tree The owner tree
6765         * @param {Node} this This node
6766         * @param {Node} node The child node to be removed
6767         */
6768        "beforeremove" : true,
6769        /**
6770         * @event beforemove
6771         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6772         * @param {Tree} tree The owner tree
6773         * @param {Node} this This node
6774         * @param {Node} oldParent The parent of this node
6775         * @param {Node} newParent The new parent this node is moving to
6776         * @param {Number} index The index it is being moved to
6777         */
6778        "beforemove" : true,
6779        /**
6780         * @event beforeinsert
6781         * Fires before a new child is inserted, return false to cancel the insert.
6782         * @param {Tree} tree The owner tree
6783         * @param {Node} this This node
6784         * @param {Node} node The child node to be inserted
6785         * @param {Node} refNode The child node the node is being inserted before
6786         */
6787        "beforeinsert" : true
6788    });
6789     this.listeners = this.attributes.listeners;
6790     Roo.data.Node.superclass.constructor.call(this);
6791 };
6792
6793 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6794     fireEvent : function(evtName){
6795         // first do standard event for this node
6796         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6797             return false;
6798         }
6799         // then bubble it up to the tree if the event wasn't cancelled
6800         var ot = this.getOwnerTree();
6801         if(ot){
6802             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6803                 return false;
6804             }
6805         }
6806         return true;
6807     },
6808
6809     /**
6810      * Returns true if this node is a leaf
6811      * @return {Boolean}
6812      */
6813     isLeaf : function(){
6814         return this.leaf === true;
6815     },
6816
6817     // private
6818     setFirstChild : function(node){
6819         this.firstChild = node;
6820     },
6821
6822     //private
6823     setLastChild : function(node){
6824         this.lastChild = node;
6825     },
6826
6827
6828     /**
6829      * Returns true if this node is the last child of its parent
6830      * @return {Boolean}
6831      */
6832     isLast : function(){
6833        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6834     },
6835
6836     /**
6837      * Returns true if this node is the first child of its parent
6838      * @return {Boolean}
6839      */
6840     isFirst : function(){
6841        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6842     },
6843
6844     hasChildNodes : function(){
6845         return !this.isLeaf() && this.childNodes.length > 0;
6846     },
6847
6848     /**
6849      * Insert node(s) as the last child node of this node.
6850      * @param {Node/Array} node The node or Array of nodes to append
6851      * @return {Node} The appended node if single append, or null if an array was passed
6852      */
6853     appendChild : function(node){
6854         var multi = false;
6855         if(node instanceof Array){
6856             multi = node;
6857         }else if(arguments.length > 1){
6858             multi = arguments;
6859         }
6860         // if passed an array or multiple args do them one by one
6861         if(multi){
6862             for(var i = 0, len = multi.length; i < len; i++) {
6863                 this.appendChild(multi[i]);
6864             }
6865         }else{
6866             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6867                 return false;
6868             }
6869             var index = this.childNodes.length;
6870             var oldParent = node.parentNode;
6871             // it's a move, make sure we move it cleanly
6872             if(oldParent){
6873                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6874                     return false;
6875                 }
6876                 oldParent.removeChild(node);
6877             }
6878             index = this.childNodes.length;
6879             if(index == 0){
6880                 this.setFirstChild(node);
6881             }
6882             this.childNodes.push(node);
6883             node.parentNode = this;
6884             var ps = this.childNodes[index-1];
6885             if(ps){
6886                 node.previousSibling = ps;
6887                 ps.nextSibling = node;
6888             }else{
6889                 node.previousSibling = null;
6890             }
6891             node.nextSibling = null;
6892             this.setLastChild(node);
6893             node.setOwnerTree(this.getOwnerTree());
6894             this.fireEvent("append", this.ownerTree, this, node, index);
6895             if(oldParent){
6896                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6897             }
6898             return node;
6899         }
6900     },
6901
6902     /**
6903      * Removes a child node from this node.
6904      * @param {Node} node The node to remove
6905      * @return {Node} The removed node
6906      */
6907     removeChild : function(node){
6908         var index = this.childNodes.indexOf(node);
6909         if(index == -1){
6910             return false;
6911         }
6912         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6913             return false;
6914         }
6915
6916         // remove it from childNodes collection
6917         this.childNodes.splice(index, 1);
6918
6919         // update siblings
6920         if(node.previousSibling){
6921             node.previousSibling.nextSibling = node.nextSibling;
6922         }
6923         if(node.nextSibling){
6924             node.nextSibling.previousSibling = node.previousSibling;
6925         }
6926
6927         // update child refs
6928         if(this.firstChild == node){
6929             this.setFirstChild(node.nextSibling);
6930         }
6931         if(this.lastChild == node){
6932             this.setLastChild(node.previousSibling);
6933         }
6934
6935         node.setOwnerTree(null);
6936         // clear any references from the node
6937         node.parentNode = null;
6938         node.previousSibling = null;
6939         node.nextSibling = null;
6940         this.fireEvent("remove", this.ownerTree, this, node);
6941         return node;
6942     },
6943
6944     /**
6945      * Inserts the first node before the second node in this nodes childNodes collection.
6946      * @param {Node} node The node to insert
6947      * @param {Node} refNode The node to insert before (if null the node is appended)
6948      * @return {Node} The inserted node
6949      */
6950     insertBefore : function(node, refNode){
6951         if(!refNode){ // like standard Dom, refNode can be null for append
6952             return this.appendChild(node);
6953         }
6954         // nothing to do
6955         if(node == refNode){
6956             return false;
6957         }
6958
6959         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6960             return false;
6961         }
6962         var index = this.childNodes.indexOf(refNode);
6963         var oldParent = node.parentNode;
6964         var refIndex = index;
6965
6966         // when moving internally, indexes will change after remove
6967         if(oldParent == this && this.childNodes.indexOf(node) < index){
6968             refIndex--;
6969         }
6970
6971         // it's a move, make sure we move it cleanly
6972         if(oldParent){
6973             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6974                 return false;
6975             }
6976             oldParent.removeChild(node);
6977         }
6978         if(refIndex == 0){
6979             this.setFirstChild(node);
6980         }
6981         this.childNodes.splice(refIndex, 0, node);
6982         node.parentNode = this;
6983         var ps = this.childNodes[refIndex-1];
6984         if(ps){
6985             node.previousSibling = ps;
6986             ps.nextSibling = node;
6987         }else{
6988             node.previousSibling = null;
6989         }
6990         node.nextSibling = refNode;
6991         refNode.previousSibling = node;
6992         node.setOwnerTree(this.getOwnerTree());
6993         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6994         if(oldParent){
6995             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6996         }
6997         return node;
6998     },
6999
7000     /**
7001      * Returns the child node at the specified index.
7002      * @param {Number} index
7003      * @return {Node}
7004      */
7005     item : function(index){
7006         return this.childNodes[index];
7007     },
7008
7009     /**
7010      * Replaces one child node in this node with another.
7011      * @param {Node} newChild The replacement node
7012      * @param {Node} oldChild The node to replace
7013      * @return {Node} The replaced node
7014      */
7015     replaceChild : function(newChild, oldChild){
7016         this.insertBefore(newChild, oldChild);
7017         this.removeChild(oldChild);
7018         return oldChild;
7019     },
7020
7021     /**
7022      * Returns the index of a child node
7023      * @param {Node} node
7024      * @return {Number} The index of the node or -1 if it was not found
7025      */
7026     indexOf : function(child){
7027         return this.childNodes.indexOf(child);
7028     },
7029
7030     /**
7031      * Returns the tree this node is in.
7032      * @return {Tree}
7033      */
7034     getOwnerTree : function(){
7035         // if it doesn't have one, look for one
7036         if(!this.ownerTree){
7037             var p = this;
7038             while(p){
7039                 if(p.ownerTree){
7040                     this.ownerTree = p.ownerTree;
7041                     break;
7042                 }
7043                 p = p.parentNode;
7044             }
7045         }
7046         return this.ownerTree;
7047     },
7048
7049     /**
7050      * Returns depth of this node (the root node has a depth of 0)
7051      * @return {Number}
7052      */
7053     getDepth : function(){
7054         var depth = 0;
7055         var p = this;
7056         while(p.parentNode){
7057             ++depth;
7058             p = p.parentNode;
7059         }
7060         return depth;
7061     },
7062
7063     // private
7064     setOwnerTree : function(tree){
7065         // if it's move, we need to update everyone
7066         if(tree != this.ownerTree){
7067             if(this.ownerTree){
7068                 this.ownerTree.unregisterNode(this);
7069             }
7070             this.ownerTree = tree;
7071             var cs = this.childNodes;
7072             for(var i = 0, len = cs.length; i < len; i++) {
7073                 cs[i].setOwnerTree(tree);
7074             }
7075             if(tree){
7076                 tree.registerNode(this);
7077             }
7078         }
7079     },
7080
7081     /**
7082      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7083      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7084      * @return {String} The path
7085      */
7086     getPath : function(attr){
7087         attr = attr || "id";
7088         var p = this.parentNode;
7089         var b = [this.attributes[attr]];
7090         while(p){
7091             b.unshift(p.attributes[attr]);
7092             p = p.parentNode;
7093         }
7094         var sep = this.getOwnerTree().pathSeparator;
7095         return sep + b.join(sep);
7096     },
7097
7098     /**
7099      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7100      * function call will be the scope provided or the current node. The arguments to the function
7101      * will be the args provided or the current node. If the function returns false at any point,
7102      * the bubble is stopped.
7103      * @param {Function} fn The function to call
7104      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7105      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7106      */
7107     bubble : function(fn, scope, args){
7108         var p = this;
7109         while(p){
7110             if(fn.call(scope || p, args || p) === false){
7111                 break;
7112             }
7113             p = p.parentNode;
7114         }
7115     },
7116
7117     /**
7118      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7119      * function call will be the scope provided or the current node. The arguments to the function
7120      * will be the args provided or the current node. If the function returns false at any point,
7121      * the cascade is stopped on that branch.
7122      * @param {Function} fn The function to call
7123      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7124      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7125      */
7126     cascade : function(fn, scope, args){
7127         if(fn.call(scope || this, args || this) !== false){
7128             var cs = this.childNodes;
7129             for(var i = 0, len = cs.length; i < len; i++) {
7130                 cs[i].cascade(fn, scope, args);
7131             }
7132         }
7133     },
7134
7135     /**
7136      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7137      * function call will be the scope provided or the current node. The arguments to the function
7138      * will be the args provided or the current node. If the function returns false at any point,
7139      * the iteration stops.
7140      * @param {Function} fn The function to call
7141      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7142      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7143      */
7144     eachChild : function(fn, scope, args){
7145         var cs = this.childNodes;
7146         for(var i = 0, len = cs.length; i < len; i++) {
7147                 if(fn.call(scope || this, args || cs[i]) === false){
7148                     break;
7149                 }
7150         }
7151     },
7152
7153     /**
7154      * Finds the first child that has the attribute with the specified value.
7155      * @param {String} attribute The attribute name
7156      * @param {Mixed} value The value to search for
7157      * @return {Node} The found child or null if none was found
7158      */
7159     findChild : function(attribute, value){
7160         var cs = this.childNodes;
7161         for(var i = 0, len = cs.length; i < len; i++) {
7162                 if(cs[i].attributes[attribute] == value){
7163                     return cs[i];
7164                 }
7165         }
7166         return null;
7167     },
7168
7169     /**
7170      * Finds the first child by a custom function. The child matches if the function passed
7171      * returns true.
7172      * @param {Function} fn
7173      * @param {Object} scope (optional)
7174      * @return {Node} The found child or null if none was found
7175      */
7176     findChildBy : function(fn, scope){
7177         var cs = this.childNodes;
7178         for(var i = 0, len = cs.length; i < len; i++) {
7179                 if(fn.call(scope||cs[i], cs[i]) === true){
7180                     return cs[i];
7181                 }
7182         }
7183         return null;
7184     },
7185
7186     /**
7187      * Sorts this nodes children using the supplied sort function
7188      * @param {Function} fn
7189      * @param {Object} scope (optional)
7190      */
7191     sort : function(fn, scope){
7192         var cs = this.childNodes;
7193         var len = cs.length;
7194         if(len > 0){
7195             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7196             cs.sort(sortFn);
7197             for(var i = 0; i < len; i++){
7198                 var n = cs[i];
7199                 n.previousSibling = cs[i-1];
7200                 n.nextSibling = cs[i+1];
7201                 if(i == 0){
7202                     this.setFirstChild(n);
7203                 }
7204                 if(i == len-1){
7205                     this.setLastChild(n);
7206                 }
7207             }
7208         }
7209     },
7210
7211     /**
7212      * Returns true if this node is an ancestor (at any point) of the passed node.
7213      * @param {Node} node
7214      * @return {Boolean}
7215      */
7216     contains : function(node){
7217         return node.isAncestor(this);
7218     },
7219
7220     /**
7221      * Returns true if the passed node is an ancestor (at any point) of this node.
7222      * @param {Node} node
7223      * @return {Boolean}
7224      */
7225     isAncestor : function(node){
7226         var p = this.parentNode;
7227         while(p){
7228             if(p == node){
7229                 return true;
7230             }
7231             p = p.parentNode;
7232         }
7233         return false;
7234     },
7235
7236     toString : function(){
7237         return "[Node"+(this.id?" "+this.id:"")+"]";
7238     }
7239 });/*
7240  * Based on:
7241  * Ext JS Library 1.1.1
7242  * Copyright(c) 2006-2007, Ext JS, LLC.
7243  *
7244  * Originally Released Under LGPL - original licence link has changed is not relivant.
7245  *
7246  * Fork - LGPL
7247  * <script type="text/javascript">
7248  */
7249  
7250
7251 /**
7252  * @class Roo.ComponentMgr
7253  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7254  * @singleton
7255  */
7256 Roo.ComponentMgr = function(){
7257     var all = new Roo.util.MixedCollection();
7258
7259     return {
7260         /**
7261          * Registers a component.
7262          * @param {Roo.Component} c The component
7263          */
7264         register : function(c){
7265             all.add(c);
7266         },
7267
7268         /**
7269          * Unregisters a component.
7270          * @param {Roo.Component} c The component
7271          */
7272         unregister : function(c){
7273             all.remove(c);
7274         },
7275
7276         /**
7277          * Returns a component by id
7278          * @param {String} id The component id
7279          */
7280         get : function(id){
7281             return all.get(id);
7282         },
7283
7284         /**
7285          * Registers a function that will be called when a specified component is added to ComponentMgr
7286          * @param {String} id The component id
7287          * @param {Funtction} fn The callback function
7288          * @param {Object} scope The scope of the callback
7289          */
7290         onAvailable : function(id, fn, scope){
7291             all.on("add", function(index, o){
7292                 if(o.id == id){
7293                     fn.call(scope || o, o);
7294                     all.un("add", fn, scope);
7295                 }
7296             });
7297         }
7298     };
7299 }();/*
7300  * Based on:
7301  * Ext JS Library 1.1.1
7302  * Copyright(c) 2006-2007, Ext JS, LLC.
7303  *
7304  * Originally Released Under LGPL - original licence link has changed is not relivant.
7305  *
7306  * Fork - LGPL
7307  * <script type="text/javascript">
7308  */
7309  
7310 /**
7311  * @class Roo.Component
7312  * @extends Roo.util.Observable
7313  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7314  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7315  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7316  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7317  * All visual components (widgets) that require rendering into a layout should subclass Component.
7318  * @constructor
7319  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7320  * 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
7321  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7322  */
7323 Roo.Component = function(config){
7324     config = config || {};
7325     if(config.tagName || config.dom || typeof config == "string"){ // element object
7326         config = {el: config, id: config.id || config};
7327     }
7328     this.initialConfig = config;
7329
7330     Roo.apply(this, config);
7331     this.addEvents({
7332         /**
7333          * @event disable
7334          * Fires after the component is disabled.
7335              * @param {Roo.Component} this
7336              */
7337         disable : true,
7338         /**
7339          * @event enable
7340          * Fires after the component is enabled.
7341              * @param {Roo.Component} this
7342              */
7343         enable : true,
7344         /**
7345          * @event beforeshow
7346          * Fires before the component is shown.  Return false to stop the show.
7347              * @param {Roo.Component} this
7348              */
7349         beforeshow : true,
7350         /**
7351          * @event show
7352          * Fires after the component is shown.
7353              * @param {Roo.Component} this
7354              */
7355         show : true,
7356         /**
7357          * @event beforehide
7358          * Fires before the component is hidden. Return false to stop the hide.
7359              * @param {Roo.Component} this
7360              */
7361         beforehide : true,
7362         /**
7363          * @event hide
7364          * Fires after the component is hidden.
7365              * @param {Roo.Component} this
7366              */
7367         hide : true,
7368         /**
7369          * @event beforerender
7370          * Fires before the component is rendered. Return false to stop the render.
7371              * @param {Roo.Component} this
7372              */
7373         beforerender : true,
7374         /**
7375          * @event render
7376          * Fires after the component is rendered.
7377              * @param {Roo.Component} this
7378              */
7379         render : true,
7380         /**
7381          * @event beforedestroy
7382          * Fires before the component is destroyed. Return false to stop the destroy.
7383              * @param {Roo.Component} this
7384              */
7385         beforedestroy : true,
7386         /**
7387          * @event destroy
7388          * Fires after the component is destroyed.
7389              * @param {Roo.Component} this
7390              */
7391         destroy : true
7392     });
7393     if(!this.id){
7394         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7395     }
7396     Roo.ComponentMgr.register(this);
7397     Roo.Component.superclass.constructor.call(this);
7398     this.initComponent();
7399     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7400         this.render(this.renderTo);
7401         delete this.renderTo;
7402     }
7403 };
7404
7405 /** @private */
7406 Roo.Component.AUTO_ID = 1000;
7407
7408 Roo.extend(Roo.Component, Roo.util.Observable, {
7409     /**
7410      * @scope Roo.Component.prototype
7411      * @type {Boolean}
7412      * true if this component is hidden. Read-only.
7413      */
7414     hidden : false,
7415     /**
7416      * @type {Boolean}
7417      * true if this component is disabled. Read-only.
7418      */
7419     disabled : false,
7420     /**
7421      * @type {Boolean}
7422      * true if this component has been rendered. Read-only.
7423      */
7424     rendered : false,
7425     
7426     /** @cfg {String} disableClass
7427      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7428      */
7429     disabledClass : "x-item-disabled",
7430         /** @cfg {Boolean} allowDomMove
7431          * Whether the component can move the Dom node when rendering (defaults to true).
7432          */
7433     allowDomMove : true,
7434     /** @cfg {String} hideMode
7435      * How this component should hidden. Supported values are
7436      * "visibility" (css visibility), "offsets" (negative offset position) and
7437      * "display" (css display) - defaults to "display".
7438      */
7439     hideMode: 'display',
7440
7441     /** @private */
7442     ctype : "Roo.Component",
7443
7444     /**
7445      * @cfg {String} actionMode 
7446      * which property holds the element that used for  hide() / show() / disable() / enable()
7447      * default is 'el' 
7448      */
7449     actionMode : "el",
7450
7451     /** @private */
7452     getActionEl : function(){
7453         return this[this.actionMode];
7454     },
7455
7456     initComponent : Roo.emptyFn,
7457     /**
7458      * If this is a lazy rendering component, render it to its container element.
7459      * @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.
7460      */
7461     render : function(container, position){
7462         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7463             if(!container && this.el){
7464                 this.el = Roo.get(this.el);
7465                 container = this.el.dom.parentNode;
7466                 this.allowDomMove = false;
7467             }
7468             this.container = Roo.get(container);
7469             this.rendered = true;
7470             if(position !== undefined){
7471                 if(typeof position == 'number'){
7472                     position = this.container.dom.childNodes[position];
7473                 }else{
7474                     position = Roo.getDom(position);
7475                 }
7476             }
7477             this.onRender(this.container, position || null);
7478             if(this.cls){
7479                 this.el.addClass(this.cls);
7480                 delete this.cls;
7481             }
7482             if(this.style){
7483                 this.el.applyStyles(this.style);
7484                 delete this.style;
7485             }
7486             this.fireEvent("render", this);
7487             this.afterRender(this.container);
7488             if(this.hidden){
7489                 this.hide();
7490             }
7491             if(this.disabled){
7492                 this.disable();
7493             }
7494         }
7495         return this;
7496     },
7497
7498     /** @private */
7499     // default function is not really useful
7500     onRender : function(ct, position){
7501         if(this.el){
7502             this.el = Roo.get(this.el);
7503             if(this.allowDomMove !== false){
7504                 ct.dom.insertBefore(this.el.dom, position);
7505             }
7506         }
7507     },
7508
7509     /** @private */
7510     getAutoCreate : function(){
7511         var cfg = typeof this.autoCreate == "object" ?
7512                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7513         if(this.id && !cfg.id){
7514             cfg.id = this.id;
7515         }
7516         return cfg;
7517     },
7518
7519     /** @private */
7520     afterRender : Roo.emptyFn,
7521
7522     /**
7523      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7524      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7525      */
7526     destroy : function(){
7527         if(this.fireEvent("beforedestroy", this) !== false){
7528             this.purgeListeners();
7529             this.beforeDestroy();
7530             if(this.rendered){
7531                 this.el.removeAllListeners();
7532                 this.el.remove();
7533                 if(this.actionMode == "container"){
7534                     this.container.remove();
7535                 }
7536             }
7537             this.onDestroy();
7538             Roo.ComponentMgr.unregister(this);
7539             this.fireEvent("destroy", this);
7540         }
7541     },
7542
7543         /** @private */
7544     beforeDestroy : function(){
7545
7546     },
7547
7548         /** @private */
7549         onDestroy : function(){
7550
7551     },
7552
7553     /**
7554      * Returns the underlying {@link Roo.Element}.
7555      * @return {Roo.Element} The element
7556      */
7557     getEl : function(){
7558         return this.el;
7559     },
7560
7561     /**
7562      * Returns the id of this component.
7563      * @return {String}
7564      */
7565     getId : function(){
7566         return this.id;
7567     },
7568
7569     /**
7570      * Try to focus this component.
7571      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7572      * @return {Roo.Component} this
7573      */
7574     focus : function(selectText){
7575         if(this.rendered){
7576             this.el.focus();
7577             if(selectText === true){
7578                 this.el.dom.select();
7579             }
7580         }
7581         return this;
7582     },
7583
7584     /** @private */
7585     blur : function(){
7586         if(this.rendered){
7587             this.el.blur();
7588         }
7589         return this;
7590     },
7591
7592     /**
7593      * Disable this component.
7594      * @return {Roo.Component} this
7595      */
7596     disable : function(){
7597         if(this.rendered){
7598             this.onDisable();
7599         }
7600         this.disabled = true;
7601         this.fireEvent("disable", this);
7602         return this;
7603     },
7604
7605         // private
7606     onDisable : function(){
7607         this.getActionEl().addClass(this.disabledClass);
7608         this.el.dom.disabled = true;
7609     },
7610
7611     /**
7612      * Enable this component.
7613      * @return {Roo.Component} this
7614      */
7615     enable : function(){
7616         if(this.rendered){
7617             this.onEnable();
7618         }
7619         this.disabled = false;
7620         this.fireEvent("enable", this);
7621         return this;
7622     },
7623
7624         // private
7625     onEnable : function(){
7626         this.getActionEl().removeClass(this.disabledClass);
7627         this.el.dom.disabled = false;
7628     },
7629
7630     /**
7631      * Convenience function for setting disabled/enabled by boolean.
7632      * @param {Boolean} disabled
7633      */
7634     setDisabled : function(disabled){
7635         this[disabled ? "disable" : "enable"]();
7636     },
7637
7638     /**
7639      * Show this component.
7640      * @return {Roo.Component} this
7641      */
7642     show: function(){
7643         if(this.fireEvent("beforeshow", this) !== false){
7644             this.hidden = false;
7645             if(this.rendered){
7646                 this.onShow();
7647             }
7648             this.fireEvent("show", this);
7649         }
7650         return this;
7651     },
7652
7653     // private
7654     onShow : function(){
7655         var ae = this.getActionEl();
7656         if(this.hideMode == 'visibility'){
7657             ae.dom.style.visibility = "visible";
7658         }else if(this.hideMode == 'offsets'){
7659             ae.removeClass('x-hidden');
7660         }else{
7661             ae.dom.style.display = "";
7662         }
7663     },
7664
7665     /**
7666      * Hide this component.
7667      * @return {Roo.Component} this
7668      */
7669     hide: function(){
7670         if(this.fireEvent("beforehide", this) !== false){
7671             this.hidden = true;
7672             if(this.rendered){
7673                 this.onHide();
7674             }
7675             this.fireEvent("hide", this);
7676         }
7677         return this;
7678     },
7679
7680     // private
7681     onHide : function(){
7682         var ae = this.getActionEl();
7683         if(this.hideMode == 'visibility'){
7684             ae.dom.style.visibility = "hidden";
7685         }else if(this.hideMode == 'offsets'){
7686             ae.addClass('x-hidden');
7687         }else{
7688             ae.dom.style.display = "none";
7689         }
7690     },
7691
7692     /**
7693      * Convenience function to hide or show this component by boolean.
7694      * @param {Boolean} visible True to show, false to hide
7695      * @return {Roo.Component} this
7696      */
7697     setVisible: function(visible){
7698         if(visible) {
7699             this.show();
7700         }else{
7701             this.hide();
7702         }
7703         return this;
7704     },
7705
7706     /**
7707      * Returns true if this component is visible.
7708      */
7709     isVisible : function(){
7710         return this.getActionEl().isVisible();
7711     },
7712
7713     cloneConfig : function(overrides){
7714         overrides = overrides || {};
7715         var id = overrides.id || Roo.id();
7716         var cfg = Roo.applyIf(overrides, this.initialConfig);
7717         cfg.id = id; // prevent dup id
7718         return new this.constructor(cfg);
7719     }
7720 });/*
7721  * Based on:
7722  * Ext JS Library 1.1.1
7723  * Copyright(c) 2006-2007, Ext JS, LLC.
7724  *
7725  * Originally Released Under LGPL - original licence link has changed is not relivant.
7726  *
7727  * Fork - LGPL
7728  * <script type="text/javascript">
7729  */
7730  (function(){ 
7731 /**
7732  * @class Roo.Layer
7733  * @extends Roo.Element
7734  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7735  * automatic maintaining of shadow/shim positions.
7736  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7737  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7738  * you can pass a string with a CSS class name. False turns off the shadow.
7739  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7740  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7741  * @cfg {String} cls CSS class to add to the element
7742  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7743  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7744  * @constructor
7745  * @param {Object} config An object with config options.
7746  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7747  */
7748
7749 Roo.Layer = function(config, existingEl){
7750     config = config || {};
7751     var dh = Roo.DomHelper;
7752     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7753     if(existingEl){
7754         this.dom = Roo.getDom(existingEl);
7755     }
7756     if(!this.dom){
7757         var o = config.dh || {tag: "div", cls: "x-layer"};
7758         this.dom = dh.append(pel, o);
7759     }
7760     if(config.cls){
7761         this.addClass(config.cls);
7762     }
7763     this.constrain = config.constrain !== false;
7764     this.visibilityMode = Roo.Element.VISIBILITY;
7765     if(config.id){
7766         this.id = this.dom.id = config.id;
7767     }else{
7768         this.id = Roo.id(this.dom);
7769     }
7770     this.zindex = config.zindex || this.getZIndex();
7771     this.position("absolute", this.zindex);
7772     if(config.shadow){
7773         this.shadowOffset = config.shadowOffset || 4;
7774         this.shadow = new Roo.Shadow({
7775             offset : this.shadowOffset,
7776             mode : config.shadow
7777         });
7778     }else{
7779         this.shadowOffset = 0;
7780     }
7781     this.useShim = config.shim !== false && Roo.useShims;
7782     this.useDisplay = config.useDisplay;
7783     this.hide();
7784 };
7785
7786 var supr = Roo.Element.prototype;
7787
7788 // shims are shared among layer to keep from having 100 iframes
7789 var shims = [];
7790
7791 Roo.extend(Roo.Layer, Roo.Element, {
7792
7793     getZIndex : function(){
7794         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7795     },
7796
7797     getShim : function(){
7798         if(!this.useShim){
7799             return null;
7800         }
7801         if(this.shim){
7802             return this.shim;
7803         }
7804         var shim = shims.shift();
7805         if(!shim){
7806             shim = this.createShim();
7807             shim.enableDisplayMode('block');
7808             shim.dom.style.display = 'none';
7809             shim.dom.style.visibility = 'visible';
7810         }
7811         var pn = this.dom.parentNode;
7812         if(shim.dom.parentNode != pn){
7813             pn.insertBefore(shim.dom, this.dom);
7814         }
7815         shim.setStyle('z-index', this.getZIndex()-2);
7816         this.shim = shim;
7817         return shim;
7818     },
7819
7820     hideShim : function(){
7821         if(this.shim){
7822             this.shim.setDisplayed(false);
7823             shims.push(this.shim);
7824             delete this.shim;
7825         }
7826     },
7827
7828     disableShadow : function(){
7829         if(this.shadow){
7830             this.shadowDisabled = true;
7831             this.shadow.hide();
7832             this.lastShadowOffset = this.shadowOffset;
7833             this.shadowOffset = 0;
7834         }
7835     },
7836
7837     enableShadow : function(show){
7838         if(this.shadow){
7839             this.shadowDisabled = false;
7840             this.shadowOffset = this.lastShadowOffset;
7841             delete this.lastShadowOffset;
7842             if(show){
7843                 this.sync(true);
7844             }
7845         }
7846     },
7847
7848     // private
7849     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7850     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7851     sync : function(doShow){
7852         var sw = this.shadow;
7853         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7854             var sh = this.getShim();
7855
7856             var w = this.getWidth(),
7857                 h = this.getHeight();
7858
7859             var l = this.getLeft(true),
7860                 t = this.getTop(true);
7861
7862             if(sw && !this.shadowDisabled){
7863                 if(doShow && !sw.isVisible()){
7864                     sw.show(this);
7865                 }else{
7866                     sw.realign(l, t, w, h);
7867                 }
7868                 if(sh){
7869                     if(doShow){
7870                        sh.show();
7871                     }
7872                     // fit the shim behind the shadow, so it is shimmed too
7873                     var a = sw.adjusts, s = sh.dom.style;
7874                     s.left = (Math.min(l, l+a.l))+"px";
7875                     s.top = (Math.min(t, t+a.t))+"px";
7876                     s.width = (w+a.w)+"px";
7877                     s.height = (h+a.h)+"px";
7878                 }
7879             }else if(sh){
7880                 if(doShow){
7881                    sh.show();
7882                 }
7883                 sh.setSize(w, h);
7884                 sh.setLeftTop(l, t);
7885             }
7886             
7887         }
7888     },
7889
7890     // private
7891     destroy : function(){
7892         this.hideShim();
7893         if(this.shadow){
7894             this.shadow.hide();
7895         }
7896         this.removeAllListeners();
7897         var pn = this.dom.parentNode;
7898         if(pn){
7899             pn.removeChild(this.dom);
7900         }
7901         Roo.Element.uncache(this.id);
7902     },
7903
7904     remove : function(){
7905         this.destroy();
7906     },
7907
7908     // private
7909     beginUpdate : function(){
7910         this.updating = true;
7911     },
7912
7913     // private
7914     endUpdate : function(){
7915         this.updating = false;
7916         this.sync(true);
7917     },
7918
7919     // private
7920     hideUnders : function(negOffset){
7921         if(this.shadow){
7922             this.shadow.hide();
7923         }
7924         this.hideShim();
7925     },
7926
7927     // private
7928     constrainXY : function(){
7929         if(this.constrain){
7930             var vw = Roo.lib.Dom.getViewWidth(),
7931                 vh = Roo.lib.Dom.getViewHeight();
7932             var s = Roo.get(document).getScroll();
7933
7934             var xy = this.getXY();
7935             var x = xy[0], y = xy[1];   
7936             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7937             // only move it if it needs it
7938             var moved = false;
7939             // first validate right/bottom
7940             if((x + w) > vw+s.left){
7941                 x = vw - w - this.shadowOffset;
7942                 moved = true;
7943             }
7944             if((y + h) > vh+s.top){
7945                 y = vh - h - this.shadowOffset;
7946                 moved = true;
7947             }
7948             // then make sure top/left isn't negative
7949             if(x < s.left){
7950                 x = s.left;
7951                 moved = true;
7952             }
7953             if(y < s.top){
7954                 y = s.top;
7955                 moved = true;
7956             }
7957             if(moved){
7958                 if(this.avoidY){
7959                     var ay = this.avoidY;
7960                     if(y <= ay && (y+h) >= ay){
7961                         y = ay-h-5;   
7962                     }
7963                 }
7964                 xy = [x, y];
7965                 this.storeXY(xy);
7966                 supr.setXY.call(this, xy);
7967                 this.sync();
7968             }
7969         }
7970     },
7971
7972     isVisible : function(){
7973         return this.visible;    
7974     },
7975
7976     // private
7977     showAction : function(){
7978         this.visible = true; // track visibility to prevent getStyle calls
7979         if(this.useDisplay === true){
7980             this.setDisplayed("");
7981         }else if(this.lastXY){
7982             supr.setXY.call(this, this.lastXY);
7983         }else if(this.lastLT){
7984             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7985         }
7986     },
7987
7988     // private
7989     hideAction : function(){
7990         this.visible = false;
7991         if(this.useDisplay === true){
7992             this.setDisplayed(false);
7993         }else{
7994             this.setLeftTop(-10000,-10000);
7995         }
7996     },
7997
7998     // overridden Element method
7999     setVisible : function(v, a, d, c, e){
8000         if(v){
8001             this.showAction();
8002         }
8003         if(a && v){
8004             var cb = function(){
8005                 this.sync(true);
8006                 if(c){
8007                     c();
8008                 }
8009             }.createDelegate(this);
8010             supr.setVisible.call(this, true, true, d, cb, e);
8011         }else{
8012             if(!v){
8013                 this.hideUnders(true);
8014             }
8015             var cb = c;
8016             if(a){
8017                 cb = function(){
8018                     this.hideAction();
8019                     if(c){
8020                         c();
8021                     }
8022                 }.createDelegate(this);
8023             }
8024             supr.setVisible.call(this, v, a, d, cb, e);
8025             if(v){
8026                 this.sync(true);
8027             }else if(!a){
8028                 this.hideAction();
8029             }
8030         }
8031     },
8032
8033     storeXY : function(xy){
8034         delete this.lastLT;
8035         this.lastXY = xy;
8036     },
8037
8038     storeLeftTop : function(left, top){
8039         delete this.lastXY;
8040         this.lastLT = [left, top];
8041     },
8042
8043     // private
8044     beforeFx : function(){
8045         this.beforeAction();
8046         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8047     },
8048
8049     // private
8050     afterFx : function(){
8051         Roo.Layer.superclass.afterFx.apply(this, arguments);
8052         this.sync(this.isVisible());
8053     },
8054
8055     // private
8056     beforeAction : function(){
8057         if(!this.updating && this.shadow){
8058             this.shadow.hide();
8059         }
8060     },
8061
8062     // overridden Element method
8063     setLeft : function(left){
8064         this.storeLeftTop(left, this.getTop(true));
8065         supr.setLeft.apply(this, arguments);
8066         this.sync();
8067     },
8068
8069     setTop : function(top){
8070         this.storeLeftTop(this.getLeft(true), top);
8071         supr.setTop.apply(this, arguments);
8072         this.sync();
8073     },
8074
8075     setLeftTop : function(left, top){
8076         this.storeLeftTop(left, top);
8077         supr.setLeftTop.apply(this, arguments);
8078         this.sync();
8079     },
8080
8081     setXY : function(xy, a, d, c, e){
8082         this.fixDisplay();
8083         this.beforeAction();
8084         this.storeXY(xy);
8085         var cb = this.createCB(c);
8086         supr.setXY.call(this, xy, a, d, cb, e);
8087         if(!a){
8088             cb();
8089         }
8090     },
8091
8092     // private
8093     createCB : function(c){
8094         var el = this;
8095         return function(){
8096             el.constrainXY();
8097             el.sync(true);
8098             if(c){
8099                 c();
8100             }
8101         };
8102     },
8103
8104     // overridden Element method
8105     setX : function(x, a, d, c, e){
8106         this.setXY([x, this.getY()], a, d, c, e);
8107     },
8108
8109     // overridden Element method
8110     setY : function(y, a, d, c, e){
8111         this.setXY([this.getX(), y], a, d, c, e);
8112     },
8113
8114     // overridden Element method
8115     setSize : function(w, h, a, d, c, e){
8116         this.beforeAction();
8117         var cb = this.createCB(c);
8118         supr.setSize.call(this, w, h, a, d, cb, e);
8119         if(!a){
8120             cb();
8121         }
8122     },
8123
8124     // overridden Element method
8125     setWidth : function(w, a, d, c, e){
8126         this.beforeAction();
8127         var cb = this.createCB(c);
8128         supr.setWidth.call(this, w, a, d, cb, e);
8129         if(!a){
8130             cb();
8131         }
8132     },
8133
8134     // overridden Element method
8135     setHeight : function(h, a, d, c, e){
8136         this.beforeAction();
8137         var cb = this.createCB(c);
8138         supr.setHeight.call(this, h, a, d, cb, e);
8139         if(!a){
8140             cb();
8141         }
8142     },
8143
8144     // overridden Element method
8145     setBounds : function(x, y, w, h, a, d, c, e){
8146         this.beforeAction();
8147         var cb = this.createCB(c);
8148         if(!a){
8149             this.storeXY([x, y]);
8150             supr.setXY.call(this, [x, y]);
8151             supr.setSize.call(this, w, h, a, d, cb, e);
8152             cb();
8153         }else{
8154             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8155         }
8156         return this;
8157     },
8158     
8159     /**
8160      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8161      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8162      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8163      * @param {Number} zindex The new z-index to set
8164      * @return {this} The Layer
8165      */
8166     setZIndex : function(zindex){
8167         this.zindex = zindex;
8168         this.setStyle("z-index", zindex + 2);
8169         if(this.shadow){
8170             this.shadow.setZIndex(zindex + 1);
8171         }
8172         if(this.shim){
8173             this.shim.setStyle("z-index", zindex);
8174         }
8175     }
8176 });
8177 })();/*
8178  * Based on:
8179  * Ext JS Library 1.1.1
8180  * Copyright(c) 2006-2007, Ext JS, LLC.
8181  *
8182  * Originally Released Under LGPL - original licence link has changed is not relivant.
8183  *
8184  * Fork - LGPL
8185  * <script type="text/javascript">
8186  */
8187
8188
8189 /**
8190  * @class Roo.Shadow
8191  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8192  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8193  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8194  * @constructor
8195  * Create a new Shadow
8196  * @param {Object} config The config object
8197  */
8198 Roo.Shadow = function(config){
8199     Roo.apply(this, config);
8200     if(typeof this.mode != "string"){
8201         this.mode = this.defaultMode;
8202     }
8203     var o = this.offset, a = {h: 0};
8204     var rad = Math.floor(this.offset/2);
8205     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8206         case "drop":
8207             a.w = 0;
8208             a.l = a.t = o;
8209             a.t -= 1;
8210             if(Roo.isIE){
8211                 a.l -= this.offset + rad;
8212                 a.t -= this.offset + rad;
8213                 a.w -= rad;
8214                 a.h -= rad;
8215                 a.t += 1;
8216             }
8217         break;
8218         case "sides":
8219             a.w = (o*2);
8220             a.l = -o;
8221             a.t = o-1;
8222             if(Roo.isIE){
8223                 a.l -= (this.offset - rad);
8224                 a.t -= this.offset + rad;
8225                 a.l += 1;
8226                 a.w -= (this.offset - rad)*2;
8227                 a.w -= rad + 1;
8228                 a.h -= 1;
8229             }
8230         break;
8231         case "frame":
8232             a.w = a.h = (o*2);
8233             a.l = a.t = -o;
8234             a.t += 1;
8235             a.h -= 2;
8236             if(Roo.isIE){
8237                 a.l -= (this.offset - rad);
8238                 a.t -= (this.offset - rad);
8239                 a.l += 1;
8240                 a.w -= (this.offset + rad + 1);
8241                 a.h -= (this.offset + rad);
8242                 a.h += 1;
8243             }
8244         break;
8245     };
8246
8247     this.adjusts = a;
8248 };
8249
8250 Roo.Shadow.prototype = {
8251     /**
8252      * @cfg {String} mode
8253      * The shadow display mode.  Supports the following options:<br />
8254      * sides: Shadow displays on both sides and bottom only<br />
8255      * frame: Shadow displays equally on all four sides<br />
8256      * drop: Traditional bottom-right drop shadow (default)
8257      */
8258     /**
8259      * @cfg {String} offset
8260      * The number of pixels to offset the shadow from the element (defaults to 4)
8261      */
8262     offset: 4,
8263
8264     // private
8265     defaultMode: "drop",
8266
8267     /**
8268      * Displays the shadow under the target element
8269      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8270      */
8271     show : function(target){
8272         target = Roo.get(target);
8273         if(!this.el){
8274             this.el = Roo.Shadow.Pool.pull();
8275             if(this.el.dom.nextSibling != target.dom){
8276                 this.el.insertBefore(target);
8277             }
8278         }
8279         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8280         if(Roo.isIE){
8281             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8282         }
8283         this.realign(
8284             target.getLeft(true),
8285             target.getTop(true),
8286             target.getWidth(),
8287             target.getHeight()
8288         );
8289         this.el.dom.style.display = "block";
8290     },
8291
8292     /**
8293      * Returns true if the shadow is visible, else false
8294      */
8295     isVisible : function(){
8296         return this.el ? true : false;  
8297     },
8298
8299     /**
8300      * Direct alignment when values are already available. Show must be called at least once before
8301      * calling this method to ensure it is initialized.
8302      * @param {Number} left The target element left position
8303      * @param {Number} top The target element top position
8304      * @param {Number} width The target element width
8305      * @param {Number} height The target element height
8306      */
8307     realign : function(l, t, w, h){
8308         if(!this.el){
8309             return;
8310         }
8311         var a = this.adjusts, d = this.el.dom, s = d.style;
8312         var iea = 0;
8313         s.left = (l+a.l)+"px";
8314         s.top = (t+a.t)+"px";
8315         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8316  
8317         if(s.width != sws || s.height != shs){
8318             s.width = sws;
8319             s.height = shs;
8320             if(!Roo.isIE){
8321                 var cn = d.childNodes;
8322                 var sww = Math.max(0, (sw-12))+"px";
8323                 cn[0].childNodes[1].style.width = sww;
8324                 cn[1].childNodes[1].style.width = sww;
8325                 cn[2].childNodes[1].style.width = sww;
8326                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8327             }
8328         }
8329     },
8330
8331     /**
8332      * Hides this shadow
8333      */
8334     hide : function(){
8335         if(this.el){
8336             this.el.dom.style.display = "none";
8337             Roo.Shadow.Pool.push(this.el);
8338             delete this.el;
8339         }
8340     },
8341
8342     /**
8343      * Adjust the z-index of this shadow
8344      * @param {Number} zindex The new z-index
8345      */
8346     setZIndex : function(z){
8347         this.zIndex = z;
8348         if(this.el){
8349             this.el.setStyle("z-index", z);
8350         }
8351     }
8352 };
8353
8354 // Private utility class that manages the internal Shadow cache
8355 Roo.Shadow.Pool = function(){
8356     var p = [];
8357     var markup = Roo.isIE ?
8358                  '<div class="x-ie-shadow"></div>' :
8359                  '<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>';
8360     return {
8361         pull : function(){
8362             var sh = p.shift();
8363             if(!sh){
8364                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8365                 sh.autoBoxAdjust = false;
8366             }
8367             return sh;
8368         },
8369
8370         push : function(sh){
8371             p.push(sh);
8372         }
8373     };
8374 }();/*
8375  * Based on:
8376  * Ext JS Library 1.1.1
8377  * Copyright(c) 2006-2007, Ext JS, LLC.
8378  *
8379  * Originally Released Under LGPL - original licence link has changed is not relivant.
8380  *
8381  * Fork - LGPL
8382  * <script type="text/javascript">
8383  */
8384
8385 /**
8386  * @class Roo.BoxComponent
8387  * @extends Roo.Component
8388  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8389  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8390  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8391  * layout containers.
8392  * @constructor
8393  * @param {Roo.Element/String/Object} config The configuration options.
8394  */
8395 Roo.BoxComponent = function(config){
8396     Roo.Component.call(this, config);
8397     this.addEvents({
8398         /**
8399          * @event resize
8400          * Fires after the component is resized.
8401              * @param {Roo.Component} this
8402              * @param {Number} adjWidth The box-adjusted width that was set
8403              * @param {Number} adjHeight The box-adjusted height that was set
8404              * @param {Number} rawWidth The width that was originally specified
8405              * @param {Number} rawHeight The height that was originally specified
8406              */
8407         resize : true,
8408         /**
8409          * @event move
8410          * Fires after the component is moved.
8411              * @param {Roo.Component} this
8412              * @param {Number} x The new x position
8413              * @param {Number} y The new y position
8414              */
8415         move : true
8416     });
8417 };
8418
8419 Roo.extend(Roo.BoxComponent, Roo.Component, {
8420     // private, set in afterRender to signify that the component has been rendered
8421     boxReady : false,
8422     // private, used to defer height settings to subclasses
8423     deferHeight: false,
8424     /** @cfg {Number} width
8425      * width (optional) size of component
8426      */
8427      /** @cfg {Number} height
8428      * height (optional) size of component
8429      */
8430      
8431     /**
8432      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8433      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8434      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8435      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8436      * @return {Roo.BoxComponent} this
8437      */
8438     setSize : function(w, h){
8439         // support for standard size objects
8440         if(typeof w == 'object'){
8441             h = w.height;
8442             w = w.width;
8443         }
8444         // not rendered
8445         if(!this.boxReady){
8446             this.width = w;
8447             this.height = h;
8448             return this;
8449         }
8450
8451         // prevent recalcs when not needed
8452         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8453             return this;
8454         }
8455         this.lastSize = {width: w, height: h};
8456
8457         var adj = this.adjustSize(w, h);
8458         var aw = adj.width, ah = adj.height;
8459         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8460             var rz = this.getResizeEl();
8461             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8462                 rz.setSize(aw, ah);
8463             }else if(!this.deferHeight && ah !== undefined){
8464                 rz.setHeight(ah);
8465             }else if(aw !== undefined){
8466                 rz.setWidth(aw);
8467             }
8468             this.onResize(aw, ah, w, h);
8469             this.fireEvent('resize', this, aw, ah, w, h);
8470         }
8471         return this;
8472     },
8473
8474     /**
8475      * Gets the current size of the component's underlying element.
8476      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8477      */
8478     getSize : function(){
8479         return this.el.getSize();
8480     },
8481
8482     /**
8483      * Gets the current XY position of the component's underlying element.
8484      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8485      * @return {Array} The XY position of the element (e.g., [100, 200])
8486      */
8487     getPosition : function(local){
8488         if(local === true){
8489             return [this.el.getLeft(true), this.el.getTop(true)];
8490         }
8491         return this.xy || this.el.getXY();
8492     },
8493
8494     /**
8495      * Gets the current box measurements of the component's underlying element.
8496      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8497      * @returns {Object} box An object in the format {x, y, width, height}
8498      */
8499     getBox : function(local){
8500         var s = this.el.getSize();
8501         if(local){
8502             s.x = this.el.getLeft(true);
8503             s.y = this.el.getTop(true);
8504         }else{
8505             var xy = this.xy || this.el.getXY();
8506             s.x = xy[0];
8507             s.y = xy[1];
8508         }
8509         return s;
8510     },
8511
8512     /**
8513      * Sets the current box measurements of the component's underlying element.
8514      * @param {Object} box An object in the format {x, y, width, height}
8515      * @returns {Roo.BoxComponent} this
8516      */
8517     updateBox : function(box){
8518         this.setSize(box.width, box.height);
8519         this.setPagePosition(box.x, box.y);
8520         return this;
8521     },
8522
8523     // protected
8524     getResizeEl : function(){
8525         return this.resizeEl || this.el;
8526     },
8527
8528     // protected
8529     getPositionEl : function(){
8530         return this.positionEl || this.el;
8531     },
8532
8533     /**
8534      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8535      * This method fires the move event.
8536      * @param {Number} left The new left
8537      * @param {Number} top The new top
8538      * @returns {Roo.BoxComponent} this
8539      */
8540     setPosition : function(x, y){
8541         this.x = x;
8542         this.y = y;
8543         if(!this.boxReady){
8544             return this;
8545         }
8546         var adj = this.adjustPosition(x, y);
8547         var ax = adj.x, ay = adj.y;
8548
8549         var el = this.getPositionEl();
8550         if(ax !== undefined || ay !== undefined){
8551             if(ax !== undefined && ay !== undefined){
8552                 el.setLeftTop(ax, ay);
8553             }else if(ax !== undefined){
8554                 el.setLeft(ax);
8555             }else if(ay !== undefined){
8556                 el.setTop(ay);
8557             }
8558             this.onPosition(ax, ay);
8559             this.fireEvent('move', this, ax, ay);
8560         }
8561         return this;
8562     },
8563
8564     /**
8565      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8566      * This method fires the move event.
8567      * @param {Number} x The new x position
8568      * @param {Number} y The new y position
8569      * @returns {Roo.BoxComponent} this
8570      */
8571     setPagePosition : function(x, y){
8572         this.pageX = x;
8573         this.pageY = y;
8574         if(!this.boxReady){
8575             return;
8576         }
8577         if(x === undefined || y === undefined){ // cannot translate undefined points
8578             return;
8579         }
8580         var p = this.el.translatePoints(x, y);
8581         this.setPosition(p.left, p.top);
8582         return this;
8583     },
8584
8585     // private
8586     onRender : function(ct, position){
8587         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8588         if(this.resizeEl){
8589             this.resizeEl = Roo.get(this.resizeEl);
8590         }
8591         if(this.positionEl){
8592             this.positionEl = Roo.get(this.positionEl);
8593         }
8594     },
8595
8596     // private
8597     afterRender : function(){
8598         Roo.BoxComponent.superclass.afterRender.call(this);
8599         this.boxReady = true;
8600         this.setSize(this.width, this.height);
8601         if(this.x || this.y){
8602             this.setPosition(this.x, this.y);
8603         }
8604         if(this.pageX || this.pageY){
8605             this.setPagePosition(this.pageX, this.pageY);
8606         }
8607     },
8608
8609     /**
8610      * Force the component's size to recalculate based on the underlying element's current height and width.
8611      * @returns {Roo.BoxComponent} this
8612      */
8613     syncSize : function(){
8614         delete this.lastSize;
8615         this.setSize(this.el.getWidth(), this.el.getHeight());
8616         return this;
8617     },
8618
8619     /**
8620      * Called after the component is resized, this method is empty by default but can be implemented by any
8621      * subclass that needs to perform custom logic after a resize occurs.
8622      * @param {Number} adjWidth The box-adjusted width that was set
8623      * @param {Number} adjHeight The box-adjusted height that was set
8624      * @param {Number} rawWidth The width that was originally specified
8625      * @param {Number} rawHeight The height that was originally specified
8626      */
8627     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8628
8629     },
8630
8631     /**
8632      * Called after the component is moved, this method is empty by default but can be implemented by any
8633      * subclass that needs to perform custom logic after a move occurs.
8634      * @param {Number} x The new x position
8635      * @param {Number} y The new y position
8636      */
8637     onPosition : function(x, y){
8638
8639     },
8640
8641     // private
8642     adjustSize : function(w, h){
8643         if(this.autoWidth){
8644             w = 'auto';
8645         }
8646         if(this.autoHeight){
8647             h = 'auto';
8648         }
8649         return {width : w, height: h};
8650     },
8651
8652     // private
8653     adjustPosition : function(x, y){
8654         return {x : x, y: y};
8655     }
8656 });/*
8657  * Based on:
8658  * Ext JS Library 1.1.1
8659  * Copyright(c) 2006-2007, Ext JS, LLC.
8660  *
8661  * Originally Released Under LGPL - original licence link has changed is not relivant.
8662  *
8663  * Fork - LGPL
8664  * <script type="text/javascript">
8665  */
8666
8667
8668 /**
8669  * @class Roo.SplitBar
8670  * @extends Roo.util.Observable
8671  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8672  * <br><br>
8673  * Usage:
8674  * <pre><code>
8675 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8676                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8677 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8678 split.minSize = 100;
8679 split.maxSize = 600;
8680 split.animate = true;
8681 split.on('moved', splitterMoved);
8682 </code></pre>
8683  * @constructor
8684  * Create a new SplitBar
8685  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8686  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8687  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8688  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8689                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8690                         position of the SplitBar).
8691  */
8692 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8693     
8694     /** @private */
8695     this.el = Roo.get(dragElement, true);
8696     this.el.dom.unselectable = "on";
8697     /** @private */
8698     this.resizingEl = Roo.get(resizingElement, true);
8699
8700     /**
8701      * @private
8702      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8703      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8704      * @type Number
8705      */
8706     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8707     
8708     /**
8709      * The minimum size of the resizing element. (Defaults to 0)
8710      * @type Number
8711      */
8712     this.minSize = 0;
8713     
8714     /**
8715      * The maximum size of the resizing element. (Defaults to 2000)
8716      * @type Number
8717      */
8718     this.maxSize = 2000;
8719     
8720     /**
8721      * Whether to animate the transition to the new size
8722      * @type Boolean
8723      */
8724     this.animate = false;
8725     
8726     /**
8727      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8728      * @type Boolean
8729      */
8730     this.useShim = false;
8731     
8732     /** @private */
8733     this.shim = null;
8734     
8735     if(!existingProxy){
8736         /** @private */
8737         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8738     }else{
8739         this.proxy = Roo.get(existingProxy).dom;
8740     }
8741     /** @private */
8742     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8743     
8744     /** @private */
8745     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8746     
8747     /** @private */
8748     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8749     
8750     /** @private */
8751     this.dragSpecs = {};
8752     
8753     /**
8754      * @private The adapter to use to positon and resize elements
8755      */
8756     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8757     this.adapter.init(this);
8758     
8759     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8760         /** @private */
8761         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8762         this.el.addClass("x-splitbar-h");
8763     }else{
8764         /** @private */
8765         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8766         this.el.addClass("x-splitbar-v");
8767     }
8768     
8769     this.addEvents({
8770         /**
8771          * @event resize
8772          * Fires when the splitter is moved (alias for {@link #event-moved})
8773          * @param {Roo.SplitBar} this
8774          * @param {Number} newSize the new width or height
8775          */
8776         "resize" : true,
8777         /**
8778          * @event moved
8779          * Fires when the splitter is moved
8780          * @param {Roo.SplitBar} this
8781          * @param {Number} newSize the new width or height
8782          */
8783         "moved" : true,
8784         /**
8785          * @event beforeresize
8786          * Fires before the splitter is dragged
8787          * @param {Roo.SplitBar} this
8788          */
8789         "beforeresize" : true,
8790
8791         "beforeapply" : true
8792     });
8793
8794     Roo.util.Observable.call(this);
8795 };
8796
8797 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8798     onStartProxyDrag : function(x, y){
8799         this.fireEvent("beforeresize", this);
8800         if(!this.overlay){
8801             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8802             o.unselectable();
8803             o.enableDisplayMode("block");
8804             // all splitbars share the same overlay
8805             Roo.SplitBar.prototype.overlay = o;
8806         }
8807         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8808         this.overlay.show();
8809         Roo.get(this.proxy).setDisplayed("block");
8810         var size = this.adapter.getElementSize(this);
8811         this.activeMinSize = this.getMinimumSize();;
8812         this.activeMaxSize = this.getMaximumSize();;
8813         var c1 = size - this.activeMinSize;
8814         var c2 = Math.max(this.activeMaxSize - size, 0);
8815         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8816             this.dd.resetConstraints();
8817             this.dd.setXConstraint(
8818                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8819                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8820             );
8821             this.dd.setYConstraint(0, 0);
8822         }else{
8823             this.dd.resetConstraints();
8824             this.dd.setXConstraint(0, 0);
8825             this.dd.setYConstraint(
8826                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8827                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8828             );
8829          }
8830         this.dragSpecs.startSize = size;
8831         this.dragSpecs.startPoint = [x, y];
8832         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8833     },
8834     
8835     /** 
8836      * @private Called after the drag operation by the DDProxy
8837      */
8838     onEndProxyDrag : function(e){
8839         Roo.get(this.proxy).setDisplayed(false);
8840         var endPoint = Roo.lib.Event.getXY(e);
8841         if(this.overlay){
8842             this.overlay.hide();
8843         }
8844         var newSize;
8845         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8846             newSize = this.dragSpecs.startSize + 
8847                 (this.placement == Roo.SplitBar.LEFT ?
8848                     endPoint[0] - this.dragSpecs.startPoint[0] :
8849                     this.dragSpecs.startPoint[0] - endPoint[0]
8850                 );
8851         }else{
8852             newSize = this.dragSpecs.startSize + 
8853                 (this.placement == Roo.SplitBar.TOP ?
8854                     endPoint[1] - this.dragSpecs.startPoint[1] :
8855                     this.dragSpecs.startPoint[1] - endPoint[1]
8856                 );
8857         }
8858         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8859         if(newSize != this.dragSpecs.startSize){
8860             if(this.fireEvent('beforeapply', this, newSize) !== false){
8861                 this.adapter.setElementSize(this, newSize);
8862                 this.fireEvent("moved", this, newSize);
8863                 this.fireEvent("resize", this, newSize);
8864             }
8865         }
8866     },
8867     
8868     /**
8869      * Get the adapter this SplitBar uses
8870      * @return The adapter object
8871      */
8872     getAdapter : function(){
8873         return this.adapter;
8874     },
8875     
8876     /**
8877      * Set the adapter this SplitBar uses
8878      * @param {Object} adapter A SplitBar adapter object
8879      */
8880     setAdapter : function(adapter){
8881         this.adapter = adapter;
8882         this.adapter.init(this);
8883     },
8884     
8885     /**
8886      * Gets the minimum size for the resizing element
8887      * @return {Number} The minimum size
8888      */
8889     getMinimumSize : function(){
8890         return this.minSize;
8891     },
8892     
8893     /**
8894      * Sets the minimum size for the resizing element
8895      * @param {Number} minSize The minimum size
8896      */
8897     setMinimumSize : function(minSize){
8898         this.minSize = minSize;
8899     },
8900     
8901     /**
8902      * Gets the maximum size for the resizing element
8903      * @return {Number} The maximum size
8904      */
8905     getMaximumSize : function(){
8906         return this.maxSize;
8907     },
8908     
8909     /**
8910      * Sets the maximum size for the resizing element
8911      * @param {Number} maxSize The maximum size
8912      */
8913     setMaximumSize : function(maxSize){
8914         this.maxSize = maxSize;
8915     },
8916     
8917     /**
8918      * Sets the initialize size for the resizing element
8919      * @param {Number} size The initial size
8920      */
8921     setCurrentSize : function(size){
8922         var oldAnimate = this.animate;
8923         this.animate = false;
8924         this.adapter.setElementSize(this, size);
8925         this.animate = oldAnimate;
8926     },
8927     
8928     /**
8929      * Destroy this splitbar. 
8930      * @param {Boolean} removeEl True to remove the element
8931      */
8932     destroy : function(removeEl){
8933         if(this.shim){
8934             this.shim.remove();
8935         }
8936         this.dd.unreg();
8937         this.proxy.parentNode.removeChild(this.proxy);
8938         if(removeEl){
8939             this.el.remove();
8940         }
8941     }
8942 });
8943
8944 /**
8945  * @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.
8946  */
8947 Roo.SplitBar.createProxy = function(dir){
8948     var proxy = new Roo.Element(document.createElement("div"));
8949     proxy.unselectable();
8950     var cls = 'x-splitbar-proxy';
8951     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8952     document.body.appendChild(proxy.dom);
8953     return proxy.dom;
8954 };
8955
8956 /** 
8957  * @class Roo.SplitBar.BasicLayoutAdapter
8958  * Default Adapter. It assumes the splitter and resizing element are not positioned
8959  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8960  */
8961 Roo.SplitBar.BasicLayoutAdapter = function(){
8962 };
8963
8964 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8965     // do nothing for now
8966     init : function(s){
8967     
8968     },
8969     /**
8970      * Called before drag operations to get the current size of the resizing element. 
8971      * @param {Roo.SplitBar} s The SplitBar using this adapter
8972      */
8973      getElementSize : function(s){
8974         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8975             return s.resizingEl.getWidth();
8976         }else{
8977             return s.resizingEl.getHeight();
8978         }
8979     },
8980     
8981     /**
8982      * Called after drag operations to set the size of the resizing element.
8983      * @param {Roo.SplitBar} s The SplitBar using this adapter
8984      * @param {Number} newSize The new size to set
8985      * @param {Function} onComplete A function to be invoked when resizing is complete
8986      */
8987     setElementSize : function(s, newSize, onComplete){
8988         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8989             if(!s.animate){
8990                 s.resizingEl.setWidth(newSize);
8991                 if(onComplete){
8992                     onComplete(s, newSize);
8993                 }
8994             }else{
8995                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8996             }
8997         }else{
8998             
8999             if(!s.animate){
9000                 s.resizingEl.setHeight(newSize);
9001                 if(onComplete){
9002                     onComplete(s, newSize);
9003                 }
9004             }else{
9005                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9006             }
9007         }
9008     }
9009 };
9010
9011 /** 
9012  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9013  * @extends Roo.SplitBar.BasicLayoutAdapter
9014  * Adapter that  moves the splitter element to align with the resized sizing element. 
9015  * Used with an absolute positioned SplitBar.
9016  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9017  * document.body, make sure you assign an id to the body element.
9018  */
9019 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9020     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9021     this.container = Roo.get(container);
9022 };
9023
9024 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9025     init : function(s){
9026         this.basic.init(s);
9027     },
9028     
9029     getElementSize : function(s){
9030         return this.basic.getElementSize(s);
9031     },
9032     
9033     setElementSize : function(s, newSize, onComplete){
9034         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9035     },
9036     
9037     moveSplitter : function(s){
9038         var yes = Roo.SplitBar;
9039         switch(s.placement){
9040             case yes.LEFT:
9041                 s.el.setX(s.resizingEl.getRight());
9042                 break;
9043             case yes.RIGHT:
9044                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9045                 break;
9046             case yes.TOP:
9047                 s.el.setY(s.resizingEl.getBottom());
9048                 break;
9049             case yes.BOTTOM:
9050                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9051                 break;
9052         }
9053     }
9054 };
9055
9056 /**
9057  * Orientation constant - Create a vertical SplitBar
9058  * @static
9059  * @type Number
9060  */
9061 Roo.SplitBar.VERTICAL = 1;
9062
9063 /**
9064  * Orientation constant - Create a horizontal SplitBar
9065  * @static
9066  * @type Number
9067  */
9068 Roo.SplitBar.HORIZONTAL = 2;
9069
9070 /**
9071  * Placement constant - The resizing element is to the left of the splitter element
9072  * @static
9073  * @type Number
9074  */
9075 Roo.SplitBar.LEFT = 1;
9076
9077 /**
9078  * Placement constant - The resizing element is to the right of the splitter element
9079  * @static
9080  * @type Number
9081  */
9082 Roo.SplitBar.RIGHT = 2;
9083
9084 /**
9085  * Placement constant - The resizing element is positioned above the splitter element
9086  * @static
9087  * @type Number
9088  */
9089 Roo.SplitBar.TOP = 3;
9090
9091 /**
9092  * Placement constant - The resizing element is positioned under splitter element
9093  * @static
9094  * @type Number
9095  */
9096 Roo.SplitBar.BOTTOM = 4;
9097 /*
9098  * Based on:
9099  * Ext JS Library 1.1.1
9100  * Copyright(c) 2006-2007, Ext JS, LLC.
9101  *
9102  * Originally Released Under LGPL - original licence link has changed is not relivant.
9103  *
9104  * Fork - LGPL
9105  * <script type="text/javascript">
9106  */
9107
9108 /**
9109  * @class Roo.View
9110  * @extends Roo.util.Observable
9111  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9112  * This class also supports single and multi selection modes. <br>
9113  * Create a data model bound view:
9114  <pre><code>
9115  var store = new Roo.data.Store(...);
9116
9117  var view = new Roo.View({
9118     el : "my-element",
9119     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9120  
9121     singleSelect: true,
9122     selectedClass: "ydataview-selected",
9123     store: store
9124  });
9125
9126  // listen for node click?
9127  view.on("click", function(vw, index, node, e){
9128  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9129  });
9130
9131  // load XML data
9132  dataModel.load("foobar.xml");
9133  </code></pre>
9134  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9135  * <br><br>
9136  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9137  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9138  * 
9139  * Note: old style constructor is still suported (container, template, config)
9140  * 
9141  * @constructor
9142  * Create a new View
9143  * @param {Object} config The config object
9144  * 
9145  */
9146 Roo.View = function(config, depreciated_tpl, depreciated_config){
9147     
9148     if (typeof(depreciated_tpl) == 'undefined') {
9149         // new way.. - universal constructor.
9150         Roo.apply(this, config);
9151         this.el  = Roo.get(this.el);
9152     } else {
9153         // old format..
9154         this.el  = Roo.get(config);
9155         this.tpl = depreciated_tpl;
9156         Roo.apply(this, depreciated_config);
9157     }
9158      
9159     
9160     if(typeof(this.tpl) == "string"){
9161         this.tpl = new Roo.Template(this.tpl);
9162     } else {
9163         // support xtype ctors..
9164         this.tpl = new Roo.factory(this.tpl, Roo);
9165     }
9166     
9167     
9168     this.tpl.compile();
9169    
9170
9171      
9172     /** @private */
9173     this.addEvents({
9174         /**
9175          * @event beforeclick
9176          * Fires before a click is processed. Returns false to cancel the default action.
9177          * @param {Roo.View} this
9178          * @param {Number} index The index of the target node
9179          * @param {HTMLElement} node The target node
9180          * @param {Roo.EventObject} e The raw event object
9181          */
9182             "beforeclick" : true,
9183         /**
9184          * @event click
9185          * Fires when a template node is clicked.
9186          * @param {Roo.View} this
9187          * @param {Number} index The index of the target node
9188          * @param {HTMLElement} node The target node
9189          * @param {Roo.EventObject} e The raw event object
9190          */
9191             "click" : true,
9192         /**
9193          * @event dblclick
9194          * Fires when a template node is double clicked.
9195          * @param {Roo.View} this
9196          * @param {Number} index The index of the target node
9197          * @param {HTMLElement} node The target node
9198          * @param {Roo.EventObject} e The raw event object
9199          */
9200             "dblclick" : true,
9201         /**
9202          * @event contextmenu
9203          * Fires when a template node is right clicked.
9204          * @param {Roo.View} this
9205          * @param {Number} index The index of the target node
9206          * @param {HTMLElement} node The target node
9207          * @param {Roo.EventObject} e The raw event object
9208          */
9209             "contextmenu" : true,
9210         /**
9211          * @event selectionchange
9212          * Fires when the selected nodes change.
9213          * @param {Roo.View} this
9214          * @param {Array} selections Array of the selected nodes
9215          */
9216             "selectionchange" : true,
9217     
9218         /**
9219          * @event beforeselect
9220          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9221          * @param {Roo.View} this
9222          * @param {HTMLElement} node The node to be selected
9223          * @param {Array} selections Array of currently selected nodes
9224          */
9225             "beforeselect" : true,
9226         /**
9227          * @event preparedata
9228          * Fires on every row to render, to allow you to change the data.
9229          * @param {Roo.View} this
9230          * @param {Object} data to be rendered (change this)
9231          */
9232           "preparedata" : true
9233         });
9234
9235     this.el.on({
9236         "click": this.onClick,
9237         "dblclick": this.onDblClick,
9238         "contextmenu": this.onContextMenu,
9239         scope:this
9240     });
9241
9242     this.selections = [];
9243     this.nodes = [];
9244     this.cmp = new Roo.CompositeElementLite([]);
9245     if(this.store){
9246         this.store = Roo.factory(this.store, Roo.data);
9247         this.setStore(this.store, true);
9248     }
9249     Roo.View.superclass.constructor.call(this);
9250 };
9251
9252 Roo.extend(Roo.View, Roo.util.Observable, {
9253     
9254      /**
9255      * @cfg {Roo.data.Store} store Data store to load data from.
9256      */
9257     store : false,
9258     
9259     /**
9260      * @cfg {String|Roo.Element} el The container element.
9261      */
9262     el : '',
9263     
9264     /**
9265      * @cfg {String|Roo.Template} tpl The template used by this View 
9266      */
9267     tpl : false,
9268     /**
9269      * @cfg {String} dataName the named area of the template to use as the data area
9270      *                          Works with domtemplates roo-name="name"
9271      */
9272     dataName: false,
9273     /**
9274      * @cfg {String} selectedClass The css class to add to selected nodes
9275      */
9276     selectedClass : "x-view-selected",
9277      /**
9278      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9279      */
9280     emptyText : "",
9281     /**
9282      * @cfg {Boolean} multiSelect Allow multiple selection
9283      */
9284     multiSelect : false,
9285     /**
9286      * @cfg {Boolean} singleSelect Allow single selection
9287      */
9288     singleSelect:  false,
9289     
9290     /**
9291      * @cfg {Boolean} toggleSelect - selecting 
9292      */
9293     toggleSelect : false,
9294     
9295     /**
9296      * Returns the element this view is bound to.
9297      * @return {Roo.Element}
9298      */
9299     getEl : function(){
9300         return this.el;
9301     },
9302
9303     /**
9304      * Refreshes the view.
9305      */
9306     refresh : function(){
9307         var t = this.tpl;
9308         
9309         // if we are using something like 'domtemplate', then
9310         // the what gets used is:
9311         // t.applySubtemplate(NAME, data, wrapping data..)
9312         // the outer template then get' applied with
9313         //     the store 'extra data'
9314         // and the body get's added to the
9315         //      roo-name="data" node?
9316         //      <span class='roo-tpl-{name}'></span> ?????
9317         
9318         
9319         
9320         this.clearSelections();
9321         this.el.update("");
9322         var html = [];
9323         var records = this.store.getRange();
9324         if(records.length < 1) {
9325             
9326             // is this valid??  = should it render a template??
9327             
9328             this.el.update(this.emptyText);
9329             return;
9330         }
9331         var el = this.el;
9332         if (this.dataName) {
9333             this.el.update(t.apply(this.store.meta)); //????
9334             el = this.el.child('.roo-tpl-' + this.dataName);
9335         }
9336         
9337         for(var i = 0, len = records.length; i < len; i++){
9338             var data = this.prepareData(records[i].data, i, records[i]);
9339             this.fireEvent("preparedata", this, data, i, records[i]);
9340             html[html.length] = Roo.util.Format.trim(
9341                 this.dataName ?
9342                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9343                     t.apply(data)
9344             );
9345         }
9346         
9347         
9348         
9349         el.update(html.join(""));
9350         this.nodes = el.dom.childNodes;
9351         this.updateIndexes(0);
9352     },
9353
9354     /**
9355      * Function to override to reformat the data that is sent to
9356      * the template for each node.
9357      * DEPRICATED - use the preparedata event handler.
9358      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9359      * a JSON object for an UpdateManager bound view).
9360      */
9361     prepareData : function(data, index, record)
9362     {
9363         this.fireEvent("preparedata", this, data, index, record);
9364         return data;
9365     },
9366
9367     onUpdate : function(ds, record){
9368         this.clearSelections();
9369         var index = this.store.indexOf(record);
9370         var n = this.nodes[index];
9371         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9372         n.parentNode.removeChild(n);
9373         this.updateIndexes(index, index);
9374     },
9375
9376     
9377     
9378 // --------- FIXME     
9379     onAdd : function(ds, records, index)
9380     {
9381         this.clearSelections();
9382         if(this.nodes.length == 0){
9383             this.refresh();
9384             return;
9385         }
9386         var n = this.nodes[index];
9387         for(var i = 0, len = records.length; i < len; i++){
9388             var d = this.prepareData(records[i].data, i, records[i]);
9389             if(n){
9390                 this.tpl.insertBefore(n, d);
9391             }else{
9392                 
9393                 this.tpl.append(this.el, d);
9394             }
9395         }
9396         this.updateIndexes(index);
9397     },
9398
9399     onRemove : function(ds, record, index){
9400         this.clearSelections();
9401         var el = this.dataName  ?
9402             this.el.child('.roo-tpl-' + this.dataName) :
9403             this.el; 
9404         el.dom.removeChild(this.nodes[index]);
9405         this.updateIndexes(index);
9406     },
9407
9408     /**
9409      * Refresh an individual node.
9410      * @param {Number} index
9411      */
9412     refreshNode : function(index){
9413         this.onUpdate(this.store, this.store.getAt(index));
9414     },
9415
9416     updateIndexes : function(startIndex, endIndex){
9417         var ns = this.nodes;
9418         startIndex = startIndex || 0;
9419         endIndex = endIndex || ns.length - 1;
9420         for(var i = startIndex; i <= endIndex; i++){
9421             ns[i].nodeIndex = i;
9422         }
9423     },
9424
9425     /**
9426      * Changes the data store this view uses and refresh the view.
9427      * @param {Store} store
9428      */
9429     setStore : function(store, initial){
9430         if(!initial && this.store){
9431             this.store.un("datachanged", this.refresh);
9432             this.store.un("add", this.onAdd);
9433             this.store.un("remove", this.onRemove);
9434             this.store.un("update", this.onUpdate);
9435             this.store.un("clear", this.refresh);
9436         }
9437         if(store){
9438           
9439             store.on("datachanged", this.refresh, this);
9440             store.on("add", this.onAdd, this);
9441             store.on("remove", this.onRemove, this);
9442             store.on("update", this.onUpdate, this);
9443             store.on("clear", this.refresh, this);
9444         }
9445         
9446         if(store){
9447             this.refresh();
9448         }
9449     },
9450
9451     /**
9452      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9453      * @param {HTMLElement} node
9454      * @return {HTMLElement} The template node
9455      */
9456     findItemFromChild : function(node){
9457         var el = this.dataName  ?
9458             this.el.child('.roo-tpl-' + this.dataName,true) :
9459             this.el.dom; 
9460         
9461         if(!node || node.parentNode == el){
9462                     return node;
9463             }
9464             var p = node.parentNode;
9465             while(p && p != el){
9466             if(p.parentNode == el){
9467                 return p;
9468             }
9469             p = p.parentNode;
9470         }
9471             return null;
9472     },
9473
9474     /** @ignore */
9475     onClick : function(e){
9476         var item = this.findItemFromChild(e.getTarget());
9477         if(item){
9478             var index = this.indexOf(item);
9479             if(this.onItemClick(item, index, e) !== false){
9480                 this.fireEvent("click", this, index, item, e);
9481             }
9482         }else{
9483             this.clearSelections();
9484         }
9485     },
9486
9487     /** @ignore */
9488     onContextMenu : function(e){
9489         var item = this.findItemFromChild(e.getTarget());
9490         if(item){
9491             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9492         }
9493     },
9494
9495     /** @ignore */
9496     onDblClick : function(e){
9497         var item = this.findItemFromChild(e.getTarget());
9498         if(item){
9499             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9500         }
9501     },
9502
9503     onItemClick : function(item, index, e)
9504     {
9505         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9506             return false;
9507         }
9508         if (this.toggleSelect) {
9509             var m = this.isSelected(item) ? 'unselect' : 'select';
9510             Roo.log(m);
9511             var _t = this;
9512             _t[m](item, true, false);
9513             return true;
9514         }
9515         if(this.multiSelect || this.singleSelect){
9516             if(this.multiSelect && e.shiftKey && this.lastSelection){
9517                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9518             }else{
9519                 this.select(item, this.multiSelect && e.ctrlKey);
9520                 this.lastSelection = item;
9521             }
9522             e.preventDefault();
9523         }
9524         return true;
9525     },
9526
9527     /**
9528      * Get the number of selected nodes.
9529      * @return {Number}
9530      */
9531     getSelectionCount : function(){
9532         return this.selections.length;
9533     },
9534
9535     /**
9536      * Get the currently selected nodes.
9537      * @return {Array} An array of HTMLElements
9538      */
9539     getSelectedNodes : function(){
9540         return this.selections;
9541     },
9542
9543     /**
9544      * Get the indexes of the selected nodes.
9545      * @return {Array}
9546      */
9547     getSelectedIndexes : function(){
9548         var indexes = [], s = this.selections;
9549         for(var i = 0, len = s.length; i < len; i++){
9550             indexes.push(s[i].nodeIndex);
9551         }
9552         return indexes;
9553     },
9554
9555     /**
9556      * Clear all selections
9557      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9558      */
9559     clearSelections : function(suppressEvent){
9560         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9561             this.cmp.elements = this.selections;
9562             this.cmp.removeClass(this.selectedClass);
9563             this.selections = [];
9564             if(!suppressEvent){
9565                 this.fireEvent("selectionchange", this, this.selections);
9566             }
9567         }
9568     },
9569
9570     /**
9571      * Returns true if the passed node is selected
9572      * @param {HTMLElement/Number} node The node or node index
9573      * @return {Boolean}
9574      */
9575     isSelected : function(node){
9576         var s = this.selections;
9577         if(s.length < 1){
9578             return false;
9579         }
9580         node = this.getNode(node);
9581         return s.indexOf(node) !== -1;
9582     },
9583
9584     /**
9585      * Selects nodes.
9586      * @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
9587      * @param {Boolean} keepExisting (optional) true to keep existing selections
9588      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9589      */
9590     select : function(nodeInfo, keepExisting, suppressEvent){
9591         if(nodeInfo instanceof Array){
9592             if(!keepExisting){
9593                 this.clearSelections(true);
9594             }
9595             for(var i = 0, len = nodeInfo.length; i < len; i++){
9596                 this.select(nodeInfo[i], true, true);
9597             }
9598             return;
9599         } 
9600         var node = this.getNode(nodeInfo);
9601         if(!node || this.isSelected(node)){
9602             return; // already selected.
9603         }
9604         if(!keepExisting){
9605             this.clearSelections(true);
9606         }
9607         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9608             Roo.fly(node).addClass(this.selectedClass);
9609             this.selections.push(node);
9610             if(!suppressEvent){
9611                 this.fireEvent("selectionchange", this, this.selections);
9612             }
9613         }
9614         
9615         
9616     },
9617       /**
9618      * Unselects nodes.
9619      * @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
9620      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9621      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9622      */
9623     unselect : function(nodeInfo, keepExisting, suppressEvent)
9624     {
9625         if(nodeInfo instanceof Array){
9626             Roo.each(this.selections, function(s) {
9627                 this.unselect(s, nodeInfo);
9628             }, this);
9629             return;
9630         }
9631         var node = this.getNode(nodeInfo);
9632         if(!node || !this.isSelected(node)){
9633             Roo.log("not selected");
9634             return; // not selected.
9635         }
9636         // fireevent???
9637         var ns = [];
9638         Roo.each(this.selections, function(s) {
9639             if (s == node ) {
9640                 Roo.fly(node).removeClass(this.selectedClass);
9641
9642                 return;
9643             }
9644             ns.push(s);
9645         },this);
9646         
9647         this.selections= ns;
9648         this.fireEvent("selectionchange", this, this.selections);
9649     },
9650
9651     /**
9652      * Gets a template node.
9653      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9654      * @return {HTMLElement} The node or null if it wasn't found
9655      */
9656     getNode : function(nodeInfo){
9657         if(typeof nodeInfo == "string"){
9658             return document.getElementById(nodeInfo);
9659         }else if(typeof nodeInfo == "number"){
9660             return this.nodes[nodeInfo];
9661         }
9662         return nodeInfo;
9663     },
9664
9665     /**
9666      * Gets a range template nodes.
9667      * @param {Number} startIndex
9668      * @param {Number} endIndex
9669      * @return {Array} An array of nodes
9670      */
9671     getNodes : function(start, end){
9672         var ns = this.nodes;
9673         start = start || 0;
9674         end = typeof end == "undefined" ? ns.length - 1 : end;
9675         var nodes = [];
9676         if(start <= end){
9677             for(var i = start; i <= end; i++){
9678                 nodes.push(ns[i]);
9679             }
9680         } else{
9681             for(var i = start; i >= end; i--){
9682                 nodes.push(ns[i]);
9683             }
9684         }
9685         return nodes;
9686     },
9687
9688     /**
9689      * Finds the index of the passed node
9690      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9691      * @return {Number} The index of the node or -1
9692      */
9693     indexOf : function(node){
9694         node = this.getNode(node);
9695         if(typeof node.nodeIndex == "number"){
9696             return node.nodeIndex;
9697         }
9698         var ns = this.nodes;
9699         for(var i = 0, len = ns.length; i < len; i++){
9700             if(ns[i] == node){
9701                 return i;
9702             }
9703         }
9704         return -1;
9705     }
9706 });
9707 /*
9708  * Based on:
9709  * Ext JS Library 1.1.1
9710  * Copyright(c) 2006-2007, Ext JS, LLC.
9711  *
9712  * Originally Released Under LGPL - original licence link has changed is not relivant.
9713  *
9714  * Fork - LGPL
9715  * <script type="text/javascript">
9716  */
9717
9718 /**
9719  * @class Roo.JsonView
9720  * @extends Roo.View
9721  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9722 <pre><code>
9723 var view = new Roo.JsonView({
9724     container: "my-element",
9725     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9726     multiSelect: true, 
9727     jsonRoot: "data" 
9728 });
9729
9730 // listen for node click?
9731 view.on("click", function(vw, index, node, e){
9732     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9733 });
9734
9735 // direct load of JSON data
9736 view.load("foobar.php");
9737
9738 // Example from my blog list
9739 var tpl = new Roo.Template(
9740     '&lt;div class="entry"&gt;' +
9741     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9742     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9743     "&lt;/div&gt;&lt;hr /&gt;"
9744 );
9745
9746 var moreView = new Roo.JsonView({
9747     container :  "entry-list", 
9748     template : tpl,
9749     jsonRoot: "posts"
9750 });
9751 moreView.on("beforerender", this.sortEntries, this);
9752 moreView.load({
9753     url: "/blog/get-posts.php",
9754     params: "allposts=true",
9755     text: "Loading Blog Entries..."
9756 });
9757 </code></pre>
9758
9759 * Note: old code is supported with arguments : (container, template, config)
9760
9761
9762  * @constructor
9763  * Create a new JsonView
9764  * 
9765  * @param {Object} config The config object
9766  * 
9767  */
9768 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9769     
9770     
9771     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9772
9773     var um = this.el.getUpdateManager();
9774     um.setRenderer(this);
9775     um.on("update", this.onLoad, this);
9776     um.on("failure", this.onLoadException, this);
9777
9778     /**
9779      * @event beforerender
9780      * Fires before rendering of the downloaded JSON data.
9781      * @param {Roo.JsonView} this
9782      * @param {Object} data The JSON data loaded
9783      */
9784     /**
9785      * @event load
9786      * Fires when data is loaded.
9787      * @param {Roo.JsonView} this
9788      * @param {Object} data The JSON data loaded
9789      * @param {Object} response The raw Connect response object
9790      */
9791     /**
9792      * @event loadexception
9793      * Fires when loading fails.
9794      * @param {Roo.JsonView} this
9795      * @param {Object} response The raw Connect response object
9796      */
9797     this.addEvents({
9798         'beforerender' : true,
9799         'load' : true,
9800         'loadexception' : true
9801     });
9802 };
9803 Roo.extend(Roo.JsonView, Roo.View, {
9804     /**
9805      * @type {String} The root property in the loaded JSON object that contains the data
9806      */
9807     jsonRoot : "",
9808
9809     /**
9810      * Refreshes the view.
9811      */
9812     refresh : function(){
9813         this.clearSelections();
9814         this.el.update("");
9815         var html = [];
9816         var o = this.jsonData;
9817         if(o && o.length > 0){
9818             for(var i = 0, len = o.length; i < len; i++){
9819                 var data = this.prepareData(o[i], i, o);
9820                 html[html.length] = this.tpl.apply(data);
9821             }
9822         }else{
9823             html.push(this.emptyText);
9824         }
9825         this.el.update(html.join(""));
9826         this.nodes = this.el.dom.childNodes;
9827         this.updateIndexes(0);
9828     },
9829
9830     /**
9831      * 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.
9832      * @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:
9833      <pre><code>
9834      view.load({
9835          url: "your-url.php",
9836          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9837          callback: yourFunction,
9838          scope: yourObject, //(optional scope)
9839          discardUrl: false,
9840          nocache: false,
9841          text: "Loading...",
9842          timeout: 30,
9843          scripts: false
9844      });
9845      </code></pre>
9846      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9847      * 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.
9848      * @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}
9849      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9850      * @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.
9851      */
9852     load : function(){
9853         var um = this.el.getUpdateManager();
9854         um.update.apply(um, arguments);
9855     },
9856
9857     render : function(el, response){
9858         this.clearSelections();
9859         this.el.update("");
9860         var o;
9861         try{
9862             o = Roo.util.JSON.decode(response.responseText);
9863             if(this.jsonRoot){
9864                 
9865                 o = o[this.jsonRoot];
9866             }
9867         } catch(e){
9868         }
9869         /**
9870          * The current JSON data or null
9871          */
9872         this.jsonData = o;
9873         this.beforeRender();
9874         this.refresh();
9875     },
9876
9877 /**
9878  * Get the number of records in the current JSON dataset
9879  * @return {Number}
9880  */
9881     getCount : function(){
9882         return this.jsonData ? this.jsonData.length : 0;
9883     },
9884
9885 /**
9886  * Returns the JSON object for the specified node(s)
9887  * @param {HTMLElement/Array} node The node or an array of nodes
9888  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9889  * you get the JSON object for the node
9890  */
9891     getNodeData : function(node){
9892         if(node instanceof Array){
9893             var data = [];
9894             for(var i = 0, len = node.length; i < len; i++){
9895                 data.push(this.getNodeData(node[i]));
9896             }
9897             return data;
9898         }
9899         return this.jsonData[this.indexOf(node)] || null;
9900     },
9901
9902     beforeRender : function(){
9903         this.snapshot = this.jsonData;
9904         if(this.sortInfo){
9905             this.sort.apply(this, this.sortInfo);
9906         }
9907         this.fireEvent("beforerender", this, this.jsonData);
9908     },
9909
9910     onLoad : function(el, o){
9911         this.fireEvent("load", this, this.jsonData, o);
9912     },
9913
9914     onLoadException : function(el, o){
9915         this.fireEvent("loadexception", this, o);
9916     },
9917
9918 /**
9919  * Filter the data by a specific property.
9920  * @param {String} property A property on your JSON objects
9921  * @param {String/RegExp} value Either string that the property values
9922  * should start with, or a RegExp to test against the property
9923  */
9924     filter : function(property, value){
9925         if(this.jsonData){
9926             var data = [];
9927             var ss = this.snapshot;
9928             if(typeof value == "string"){
9929                 var vlen = value.length;
9930                 if(vlen == 0){
9931                     this.clearFilter();
9932                     return;
9933                 }
9934                 value = value.toLowerCase();
9935                 for(var i = 0, len = ss.length; i < len; i++){
9936                     var o = ss[i];
9937                     if(o[property].substr(0, vlen).toLowerCase() == value){
9938                         data.push(o);
9939                     }
9940                 }
9941             } else if(value.exec){ // regex?
9942                 for(var i = 0, len = ss.length; i < len; i++){
9943                     var o = ss[i];
9944                     if(value.test(o[property])){
9945                         data.push(o);
9946                     }
9947                 }
9948             } else{
9949                 return;
9950             }
9951             this.jsonData = data;
9952             this.refresh();
9953         }
9954     },
9955
9956 /**
9957  * Filter by a function. The passed function will be called with each
9958  * object in the current dataset. If the function returns true the value is kept,
9959  * otherwise it is filtered.
9960  * @param {Function} fn
9961  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9962  */
9963     filterBy : function(fn, scope){
9964         if(this.jsonData){
9965             var data = [];
9966             var ss = this.snapshot;
9967             for(var i = 0, len = ss.length; i < len; i++){
9968                 var o = ss[i];
9969                 if(fn.call(scope || this, o)){
9970                     data.push(o);
9971                 }
9972             }
9973             this.jsonData = data;
9974             this.refresh();
9975         }
9976     },
9977
9978 /**
9979  * Clears the current filter.
9980  */
9981     clearFilter : function(){
9982         if(this.snapshot && this.jsonData != this.snapshot){
9983             this.jsonData = this.snapshot;
9984             this.refresh();
9985         }
9986     },
9987
9988
9989 /**
9990  * Sorts the data for this view and refreshes it.
9991  * @param {String} property A property on your JSON objects to sort on
9992  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9993  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9994  */
9995     sort : function(property, dir, sortType){
9996         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9997         if(this.jsonData){
9998             var p = property;
9999             var dsc = dir && dir.toLowerCase() == "desc";
10000             var f = function(o1, o2){
10001                 var v1 = sortType ? sortType(o1[p]) : o1[p];
10002                 var v2 = sortType ? sortType(o2[p]) : o2[p];
10003                 ;
10004                 if(v1 < v2){
10005                     return dsc ? +1 : -1;
10006                 } else if(v1 > v2){
10007                     return dsc ? -1 : +1;
10008                 } else{
10009                     return 0;
10010                 }
10011             };
10012             this.jsonData.sort(f);
10013             this.refresh();
10014             if(this.jsonData != this.snapshot){
10015                 this.snapshot.sort(f);
10016             }
10017         }
10018     }
10019 });/*
10020  * Based on:
10021  * Ext JS Library 1.1.1
10022  * Copyright(c) 2006-2007, Ext JS, LLC.
10023  *
10024  * Originally Released Under LGPL - original licence link has changed is not relivant.
10025  *
10026  * Fork - LGPL
10027  * <script type="text/javascript">
10028  */
10029  
10030
10031 /**
10032  * @class Roo.ColorPalette
10033  * @extends Roo.Component
10034  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10035  * Here's an example of typical usage:
10036  * <pre><code>
10037 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10038 cp.render('my-div');
10039
10040 cp.on('select', function(palette, selColor){
10041     // do something with selColor
10042 });
10043 </code></pre>
10044  * @constructor
10045  * Create a new ColorPalette
10046  * @param {Object} config The config object
10047  */
10048 Roo.ColorPalette = function(config){
10049     Roo.ColorPalette.superclass.constructor.call(this, config);
10050     this.addEvents({
10051         /**
10052              * @event select
10053              * Fires when a color is selected
10054              * @param {ColorPalette} this
10055              * @param {String} color The 6-digit color hex code (without the # symbol)
10056              */
10057         select: true
10058     });
10059
10060     if(this.handler){
10061         this.on("select", this.handler, this.scope, true);
10062     }
10063 };
10064 Roo.extend(Roo.ColorPalette, Roo.Component, {
10065     /**
10066      * @cfg {String} itemCls
10067      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10068      */
10069     itemCls : "x-color-palette",
10070     /**
10071      * @cfg {String} value
10072      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10073      * the hex codes are case-sensitive.
10074      */
10075     value : null,
10076     clickEvent:'click',
10077     // private
10078     ctype: "Roo.ColorPalette",
10079
10080     /**
10081      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10082      */
10083     allowReselect : false,
10084
10085     /**
10086      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10087      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10088      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10089      * of colors with the width setting until the box is symmetrical.</p>
10090      * <p>You can override individual colors if needed:</p>
10091      * <pre><code>
10092 var cp = new Roo.ColorPalette();
10093 cp.colors[0] = "FF0000";  // change the first box to red
10094 </code></pre>
10095
10096 Or you can provide a custom array of your own for complete control:
10097 <pre><code>
10098 var cp = new Roo.ColorPalette();
10099 cp.colors = ["000000", "993300", "333300"];
10100 </code></pre>
10101      * @type Array
10102      */
10103     colors : [
10104         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10105         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10106         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10107         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10108         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10109     ],
10110
10111     // private
10112     onRender : function(container, position){
10113         var t = new Roo.MasterTemplate(
10114             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10115         );
10116         var c = this.colors;
10117         for(var i = 0, len = c.length; i < len; i++){
10118             t.add([c[i]]);
10119         }
10120         var el = document.createElement("div");
10121         el.className = this.itemCls;
10122         t.overwrite(el);
10123         container.dom.insertBefore(el, position);
10124         this.el = Roo.get(el);
10125         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10126         if(this.clickEvent != 'click'){
10127             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10128         }
10129     },
10130
10131     // private
10132     afterRender : function(){
10133         Roo.ColorPalette.superclass.afterRender.call(this);
10134         if(this.value){
10135             var s = this.value;
10136             this.value = null;
10137             this.select(s);
10138         }
10139     },
10140
10141     // private
10142     handleClick : function(e, t){
10143         e.preventDefault();
10144         if(!this.disabled){
10145             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10146             this.select(c.toUpperCase());
10147         }
10148     },
10149
10150     /**
10151      * Selects the specified color in the palette (fires the select event)
10152      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10153      */
10154     select : function(color){
10155         color = color.replace("#", "");
10156         if(color != this.value || this.allowReselect){
10157             var el = this.el;
10158             if(this.value){
10159                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10160             }
10161             el.child("a.color-"+color).addClass("x-color-palette-sel");
10162             this.value = color;
10163             this.fireEvent("select", this, color);
10164         }
10165     }
10166 });/*
10167  * Based on:
10168  * Ext JS Library 1.1.1
10169  * Copyright(c) 2006-2007, Ext JS, LLC.
10170  *
10171  * Originally Released Under LGPL - original licence link has changed is not relivant.
10172  *
10173  * Fork - LGPL
10174  * <script type="text/javascript">
10175  */
10176  
10177 /**
10178  * @class Roo.DatePicker
10179  * @extends Roo.Component
10180  * Simple date picker class.
10181  * @constructor
10182  * Create a new DatePicker
10183  * @param {Object} config The config object
10184  */
10185 Roo.DatePicker = function(config){
10186     Roo.DatePicker.superclass.constructor.call(this, config);
10187
10188     this.value = config && config.value ?
10189                  config.value.clearTime() : new Date().clearTime();
10190
10191     this.addEvents({
10192         /**
10193              * @event select
10194              * Fires when a date is selected
10195              * @param {DatePicker} this
10196              * @param {Date} date The selected date
10197              */
10198         'select': true,
10199         /**
10200              * @event monthchange
10201              * Fires when the displayed month changes 
10202              * @param {DatePicker} this
10203              * @param {Date} date The selected month
10204              */
10205         'monthchange': true
10206     });
10207
10208     if(this.handler){
10209         this.on("select", this.handler,  this.scope || this);
10210     }
10211     // build the disabledDatesRE
10212     if(!this.disabledDatesRE && this.disabledDates){
10213         var dd = this.disabledDates;
10214         var re = "(?:";
10215         for(var i = 0; i < dd.length; i++){
10216             re += dd[i];
10217             if(i != dd.length-1) re += "|";
10218         }
10219         this.disabledDatesRE = new RegExp(re + ")");
10220     }
10221 };
10222
10223 Roo.extend(Roo.DatePicker, Roo.Component, {
10224     /**
10225      * @cfg {String} todayText
10226      * The text to display on the button that selects the current date (defaults to "Today")
10227      */
10228     todayText : "Today",
10229     /**
10230      * @cfg {String} okText
10231      * The text to display on the ok button
10232      */
10233     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10234     /**
10235      * @cfg {String} cancelText
10236      * The text to display on the cancel button
10237      */
10238     cancelText : "Cancel",
10239     /**
10240      * @cfg {String} todayTip
10241      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10242      */
10243     todayTip : "{0} (Spacebar)",
10244     /**
10245      * @cfg {Date} minDate
10246      * Minimum allowable date (JavaScript date object, defaults to null)
10247      */
10248     minDate : null,
10249     /**
10250      * @cfg {Date} maxDate
10251      * Maximum allowable date (JavaScript date object, defaults to null)
10252      */
10253     maxDate : null,
10254     /**
10255      * @cfg {String} minText
10256      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10257      */
10258     minText : "This date is before the minimum date",
10259     /**
10260      * @cfg {String} maxText
10261      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10262      */
10263     maxText : "This date is after the maximum date",
10264     /**
10265      * @cfg {String} format
10266      * The default date format string which can be overriden for localization support.  The format must be
10267      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10268      */
10269     format : "m/d/y",
10270     /**
10271      * @cfg {Array} disabledDays
10272      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10273      */
10274     disabledDays : null,
10275     /**
10276      * @cfg {String} disabledDaysText
10277      * The tooltip to display when the date falls on a disabled day (defaults to "")
10278      */
10279     disabledDaysText : "",
10280     /**
10281      * @cfg {RegExp} disabledDatesRE
10282      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10283      */
10284     disabledDatesRE : null,
10285     /**
10286      * @cfg {String} disabledDatesText
10287      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10288      */
10289     disabledDatesText : "",
10290     /**
10291      * @cfg {Boolean} constrainToViewport
10292      * True to constrain the date picker to the viewport (defaults to true)
10293      */
10294     constrainToViewport : true,
10295     /**
10296      * @cfg {Array} monthNames
10297      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10298      */
10299     monthNames : Date.monthNames,
10300     /**
10301      * @cfg {Array} dayNames
10302      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10303      */
10304     dayNames : Date.dayNames,
10305     /**
10306      * @cfg {String} nextText
10307      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10308      */
10309     nextText: 'Next Month (Control+Right)',
10310     /**
10311      * @cfg {String} prevText
10312      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10313      */
10314     prevText: 'Previous Month (Control+Left)',
10315     /**
10316      * @cfg {String} monthYearText
10317      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10318      */
10319     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10320     /**
10321      * @cfg {Number} startDay
10322      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10323      */
10324     startDay : 0,
10325     /**
10326      * @cfg {Bool} showClear
10327      * Show a clear button (usefull for date form elements that can be blank.)
10328      */
10329     
10330     showClear: false,
10331     
10332     /**
10333      * Sets the value of the date field
10334      * @param {Date} value The date to set
10335      */
10336     setValue : function(value){
10337         var old = this.value;
10338         this.value = value.clearTime(true);
10339         if(this.el){
10340             this.update(this.value);
10341         }
10342     },
10343
10344     /**
10345      * Gets the current selected value of the date field
10346      * @return {Date} The selected date
10347      */
10348     getValue : function(){
10349         return this.value;
10350     },
10351
10352     // private
10353     focus : function(){
10354         if(this.el){
10355             this.update(this.activeDate);
10356         }
10357     },
10358
10359     // private
10360     onRender : function(container, position){
10361         var m = [
10362              '<table cellspacing="0">',
10363                 '<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>',
10364                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10365         var dn = this.dayNames;
10366         for(var i = 0; i < 7; i++){
10367             var d = this.startDay+i;
10368             if(d > 6){
10369                 d = d-7;
10370             }
10371             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10372         }
10373         m[m.length] = "</tr></thead><tbody><tr>";
10374         for(var i = 0; i < 42; i++) {
10375             if(i % 7 == 0 && i != 0){
10376                 m[m.length] = "</tr><tr>";
10377             }
10378             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10379         }
10380         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10381             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10382
10383         var el = document.createElement("div");
10384         el.className = "x-date-picker";
10385         el.innerHTML = m.join("");
10386
10387         container.dom.insertBefore(el, position);
10388
10389         this.el = Roo.get(el);
10390         this.eventEl = Roo.get(el.firstChild);
10391
10392         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10393             handler: this.showPrevMonth,
10394             scope: this,
10395             preventDefault:true,
10396             stopDefault:true
10397         });
10398
10399         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10400             handler: this.showNextMonth,
10401             scope: this,
10402             preventDefault:true,
10403             stopDefault:true
10404         });
10405
10406         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10407
10408         this.monthPicker = this.el.down('div.x-date-mp');
10409         this.monthPicker.enableDisplayMode('block');
10410         
10411         var kn = new Roo.KeyNav(this.eventEl, {
10412             "left" : function(e){
10413                 e.ctrlKey ?
10414                     this.showPrevMonth() :
10415                     this.update(this.activeDate.add("d", -1));
10416             },
10417
10418             "right" : function(e){
10419                 e.ctrlKey ?
10420                     this.showNextMonth() :
10421                     this.update(this.activeDate.add("d", 1));
10422             },
10423
10424             "up" : function(e){
10425                 e.ctrlKey ?
10426                     this.showNextYear() :
10427                     this.update(this.activeDate.add("d", -7));
10428             },
10429
10430             "down" : function(e){
10431                 e.ctrlKey ?
10432                     this.showPrevYear() :
10433                     this.update(this.activeDate.add("d", 7));
10434             },
10435
10436             "pageUp" : function(e){
10437                 this.showNextMonth();
10438             },
10439
10440             "pageDown" : function(e){
10441                 this.showPrevMonth();
10442             },
10443
10444             "enter" : function(e){
10445                 e.stopPropagation();
10446                 return true;
10447             },
10448
10449             scope : this
10450         });
10451
10452         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10453
10454         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10455
10456         this.el.unselectable();
10457         
10458         this.cells = this.el.select("table.x-date-inner tbody td");
10459         this.textNodes = this.el.query("table.x-date-inner tbody span");
10460
10461         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10462             text: "&#160;",
10463             tooltip: this.monthYearText
10464         });
10465
10466         this.mbtn.on('click', this.showMonthPicker, this);
10467         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10468
10469
10470         var today = (new Date()).dateFormat(this.format);
10471         
10472         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10473         if (this.showClear) {
10474             baseTb.add( new Roo.Toolbar.Fill());
10475         }
10476         baseTb.add({
10477             text: String.format(this.todayText, today),
10478             tooltip: String.format(this.todayTip, today),
10479             handler: this.selectToday,
10480             scope: this
10481         });
10482         
10483         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10484             
10485         //});
10486         if (this.showClear) {
10487             
10488             baseTb.add( new Roo.Toolbar.Fill());
10489             baseTb.add({
10490                 text: '&#160;',
10491                 cls: 'x-btn-icon x-btn-clear',
10492                 handler: function() {
10493                     //this.value = '';
10494                     this.fireEvent("select", this, '');
10495                 },
10496                 scope: this
10497             });
10498         }
10499         
10500         
10501         if(Roo.isIE){
10502             this.el.repaint();
10503         }
10504         this.update(this.value);
10505     },
10506
10507     createMonthPicker : function(){
10508         if(!this.monthPicker.dom.firstChild){
10509             var buf = ['<table border="0" cellspacing="0">'];
10510             for(var i = 0; i < 6; i++){
10511                 buf.push(
10512                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10513                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10514                     i == 0 ?
10515                     '<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>' :
10516                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10517                 );
10518             }
10519             buf.push(
10520                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10521                     this.okText,
10522                     '</button><button type="button" class="x-date-mp-cancel">',
10523                     this.cancelText,
10524                     '</button></td></tr>',
10525                 '</table>'
10526             );
10527             this.monthPicker.update(buf.join(''));
10528             this.monthPicker.on('click', this.onMonthClick, this);
10529             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10530
10531             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10532             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10533
10534             this.mpMonths.each(function(m, a, i){
10535                 i += 1;
10536                 if((i%2) == 0){
10537                     m.dom.xmonth = 5 + Math.round(i * .5);
10538                 }else{
10539                     m.dom.xmonth = Math.round((i-1) * .5);
10540                 }
10541             });
10542         }
10543     },
10544
10545     showMonthPicker : function(){
10546         this.createMonthPicker();
10547         var size = this.el.getSize();
10548         this.monthPicker.setSize(size);
10549         this.monthPicker.child('table').setSize(size);
10550
10551         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10552         this.updateMPMonth(this.mpSelMonth);
10553         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10554         this.updateMPYear(this.mpSelYear);
10555
10556         this.monthPicker.slideIn('t', {duration:.2});
10557     },
10558
10559     updateMPYear : function(y){
10560         this.mpyear = y;
10561         var ys = this.mpYears.elements;
10562         for(var i = 1; i <= 10; i++){
10563             var td = ys[i-1], y2;
10564             if((i%2) == 0){
10565                 y2 = y + Math.round(i * .5);
10566                 td.firstChild.innerHTML = y2;
10567                 td.xyear = y2;
10568             }else{
10569                 y2 = y - (5-Math.round(i * .5));
10570                 td.firstChild.innerHTML = y2;
10571                 td.xyear = y2;
10572             }
10573             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10574         }
10575     },
10576
10577     updateMPMonth : function(sm){
10578         this.mpMonths.each(function(m, a, i){
10579             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10580         });
10581     },
10582
10583     selectMPMonth: function(m){
10584         
10585     },
10586
10587     onMonthClick : function(e, t){
10588         e.stopEvent();
10589         var el = new Roo.Element(t), pn;
10590         if(el.is('button.x-date-mp-cancel')){
10591             this.hideMonthPicker();
10592         }
10593         else if(el.is('button.x-date-mp-ok')){
10594             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10595             this.hideMonthPicker();
10596         }
10597         else if(pn = el.up('td.x-date-mp-month', 2)){
10598             this.mpMonths.removeClass('x-date-mp-sel');
10599             pn.addClass('x-date-mp-sel');
10600             this.mpSelMonth = pn.dom.xmonth;
10601         }
10602         else if(pn = el.up('td.x-date-mp-year', 2)){
10603             this.mpYears.removeClass('x-date-mp-sel');
10604             pn.addClass('x-date-mp-sel');
10605             this.mpSelYear = pn.dom.xyear;
10606         }
10607         else if(el.is('a.x-date-mp-prev')){
10608             this.updateMPYear(this.mpyear-10);
10609         }
10610         else if(el.is('a.x-date-mp-next')){
10611             this.updateMPYear(this.mpyear+10);
10612         }
10613     },
10614
10615     onMonthDblClick : function(e, t){
10616         e.stopEvent();
10617         var el = new Roo.Element(t), pn;
10618         if(pn = el.up('td.x-date-mp-month', 2)){
10619             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10620             this.hideMonthPicker();
10621         }
10622         else if(pn = el.up('td.x-date-mp-year', 2)){
10623             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10624             this.hideMonthPicker();
10625         }
10626     },
10627
10628     hideMonthPicker : function(disableAnim){
10629         if(this.monthPicker){
10630             if(disableAnim === true){
10631                 this.monthPicker.hide();
10632             }else{
10633                 this.monthPicker.slideOut('t', {duration:.2});
10634             }
10635         }
10636     },
10637
10638     // private
10639     showPrevMonth : function(e){
10640         this.update(this.activeDate.add("mo", -1));
10641     },
10642
10643     // private
10644     showNextMonth : function(e){
10645         this.update(this.activeDate.add("mo", 1));
10646     },
10647
10648     // private
10649     showPrevYear : function(){
10650         this.update(this.activeDate.add("y", -1));
10651     },
10652
10653     // private
10654     showNextYear : function(){
10655         this.update(this.activeDate.add("y", 1));
10656     },
10657
10658     // private
10659     handleMouseWheel : function(e){
10660         var delta = e.getWheelDelta();
10661         if(delta > 0){
10662             this.showPrevMonth();
10663             e.stopEvent();
10664         } else if(delta < 0){
10665             this.showNextMonth();
10666             e.stopEvent();
10667         }
10668     },
10669
10670     // private
10671     handleDateClick : function(e, t){
10672         e.stopEvent();
10673         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10674             this.setValue(new Date(t.dateValue));
10675             this.fireEvent("select", this, this.value);
10676         }
10677     },
10678
10679     // private
10680     selectToday : function(){
10681         this.setValue(new Date().clearTime());
10682         this.fireEvent("select", this, this.value);
10683     },
10684
10685     // private
10686     update : function(date)
10687     {
10688         var vd = this.activeDate;
10689         this.activeDate = date;
10690         if(vd && this.el){
10691             var t = date.getTime();
10692             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10693                 this.cells.removeClass("x-date-selected");
10694                 this.cells.each(function(c){
10695                    if(c.dom.firstChild.dateValue == t){
10696                        c.addClass("x-date-selected");
10697                        setTimeout(function(){
10698                             try{c.dom.firstChild.focus();}catch(e){}
10699                        }, 50);
10700                        return false;
10701                    }
10702                 });
10703                 return;
10704             }
10705         }
10706         
10707         var days = date.getDaysInMonth();
10708         var firstOfMonth = date.getFirstDateOfMonth();
10709         var startingPos = firstOfMonth.getDay()-this.startDay;
10710
10711         if(startingPos <= this.startDay){
10712             startingPos += 7;
10713         }
10714
10715         var pm = date.add("mo", -1);
10716         var prevStart = pm.getDaysInMonth()-startingPos;
10717
10718         var cells = this.cells.elements;
10719         var textEls = this.textNodes;
10720         days += startingPos;
10721
10722         // convert everything to numbers so it's fast
10723         var day = 86400000;
10724         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10725         var today = new Date().clearTime().getTime();
10726         var sel = date.clearTime().getTime();
10727         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10728         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10729         var ddMatch = this.disabledDatesRE;
10730         var ddText = this.disabledDatesText;
10731         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10732         var ddaysText = this.disabledDaysText;
10733         var format = this.format;
10734
10735         var setCellClass = function(cal, cell){
10736             cell.title = "";
10737             var t = d.getTime();
10738             cell.firstChild.dateValue = t;
10739             if(t == today){
10740                 cell.className += " x-date-today";
10741                 cell.title = cal.todayText;
10742             }
10743             if(t == sel){
10744                 cell.className += " x-date-selected";
10745                 setTimeout(function(){
10746                     try{cell.firstChild.focus();}catch(e){}
10747                 }, 50);
10748             }
10749             // disabling
10750             if(t < min) {
10751                 cell.className = " x-date-disabled";
10752                 cell.title = cal.minText;
10753                 return;
10754             }
10755             if(t > max) {
10756                 cell.className = " x-date-disabled";
10757                 cell.title = cal.maxText;
10758                 return;
10759             }
10760             if(ddays){
10761                 if(ddays.indexOf(d.getDay()) != -1){
10762                     cell.title = ddaysText;
10763                     cell.className = " x-date-disabled";
10764                 }
10765             }
10766             if(ddMatch && format){
10767                 var fvalue = d.dateFormat(format);
10768                 if(ddMatch.test(fvalue)){
10769                     cell.title = ddText.replace("%0", fvalue);
10770                     cell.className = " x-date-disabled";
10771                 }
10772             }
10773         };
10774
10775         var i = 0;
10776         for(; i < startingPos; i++) {
10777             textEls[i].innerHTML = (++prevStart);
10778             d.setDate(d.getDate()+1);
10779             cells[i].className = "x-date-prevday";
10780             setCellClass(this, cells[i]);
10781         }
10782         for(; i < days; i++){
10783             intDay = i - startingPos + 1;
10784             textEls[i].innerHTML = (intDay);
10785             d.setDate(d.getDate()+1);
10786             cells[i].className = "x-date-active";
10787             setCellClass(this, cells[i]);
10788         }
10789         var extraDays = 0;
10790         for(; i < 42; i++) {
10791              textEls[i].innerHTML = (++extraDays);
10792              d.setDate(d.getDate()+1);
10793              cells[i].className = "x-date-nextday";
10794              setCellClass(this, cells[i]);
10795         }
10796
10797         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10798         this.fireEvent('monthchange', this, date);
10799         
10800         if(!this.internalRender){
10801             var main = this.el.dom.firstChild;
10802             var w = main.offsetWidth;
10803             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10804             Roo.fly(main).setWidth(w);
10805             this.internalRender = true;
10806             // opera does not respect the auto grow header center column
10807             // then, after it gets a width opera refuses to recalculate
10808             // without a second pass
10809             if(Roo.isOpera && !this.secondPass){
10810                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10811                 this.secondPass = true;
10812                 this.update.defer(10, this, [date]);
10813             }
10814         }
10815         
10816         
10817     }
10818 });        /*
10819  * Based on:
10820  * Ext JS Library 1.1.1
10821  * Copyright(c) 2006-2007, Ext JS, LLC.
10822  *
10823  * Originally Released Under LGPL - original licence link has changed is not relivant.
10824  *
10825  * Fork - LGPL
10826  * <script type="text/javascript">
10827  */
10828 /**
10829  * @class Roo.TabPanel
10830  * @extends Roo.util.Observable
10831  * A lightweight tab container.
10832  * <br><br>
10833  * Usage:
10834  * <pre><code>
10835 // basic tabs 1, built from existing content
10836 var tabs = new Roo.TabPanel("tabs1");
10837 tabs.addTab("script", "View Script");
10838 tabs.addTab("markup", "View Markup");
10839 tabs.activate("script");
10840
10841 // more advanced tabs, built from javascript
10842 var jtabs = new Roo.TabPanel("jtabs");
10843 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10844
10845 // set up the UpdateManager
10846 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10847 var updater = tab2.getUpdateManager();
10848 updater.setDefaultUrl("ajax1.htm");
10849 tab2.on('activate', updater.refresh, updater, true);
10850
10851 // Use setUrl for Ajax loading
10852 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10853 tab3.setUrl("ajax2.htm", null, true);
10854
10855 // Disabled tab
10856 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10857 tab4.disable();
10858
10859 jtabs.activate("jtabs-1");
10860  * </code></pre>
10861  * @constructor
10862  * Create a new TabPanel.
10863  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10864  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10865  */
10866 Roo.TabPanel = function(container, config){
10867     /**
10868     * The container element for this TabPanel.
10869     * @type Roo.Element
10870     */
10871     this.el = Roo.get(container, true);
10872     if(config){
10873         if(typeof config == "boolean"){
10874             this.tabPosition = config ? "bottom" : "top";
10875         }else{
10876             Roo.apply(this, config);
10877         }
10878     }
10879     if(this.tabPosition == "bottom"){
10880         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10881         this.el.addClass("x-tabs-bottom");
10882     }
10883     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10884     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10885     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10886     if(Roo.isIE){
10887         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10888     }
10889     if(this.tabPosition != "bottom"){
10890         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10891          * @type Roo.Element
10892          */
10893         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10894         this.el.addClass("x-tabs-top");
10895     }
10896     this.items = [];
10897
10898     this.bodyEl.setStyle("position", "relative");
10899
10900     this.active = null;
10901     this.activateDelegate = this.activate.createDelegate(this);
10902
10903     this.addEvents({
10904         /**
10905          * @event tabchange
10906          * Fires when the active tab changes
10907          * @param {Roo.TabPanel} this
10908          * @param {Roo.TabPanelItem} activePanel The new active tab
10909          */
10910         "tabchange": true,
10911         /**
10912          * @event beforetabchange
10913          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10914          * @param {Roo.TabPanel} this
10915          * @param {Object} e Set cancel to true on this object to cancel the tab change
10916          * @param {Roo.TabPanelItem} tab The tab being changed to
10917          */
10918         "beforetabchange" : true
10919     });
10920
10921     Roo.EventManager.onWindowResize(this.onResize, this);
10922     this.cpad = this.el.getPadding("lr");
10923     this.hiddenCount = 0;
10924
10925
10926     // toolbar on the tabbar support...
10927     if (this.toolbar) {
10928         var tcfg = this.toolbar;
10929         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10930         this.toolbar = new Roo.Toolbar(tcfg);
10931         if (Roo.isSafari) {
10932             var tbl = tcfg.container.child('table', true);
10933             tbl.setAttribute('width', '100%');
10934         }
10935         
10936     }
10937    
10938
10939
10940     Roo.TabPanel.superclass.constructor.call(this);
10941 };
10942
10943 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10944     /*
10945      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10946      */
10947     tabPosition : "top",
10948     /*
10949      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10950      */
10951     currentTabWidth : 0,
10952     /*
10953      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10954      */
10955     minTabWidth : 40,
10956     /*
10957      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10958      */
10959     maxTabWidth : 250,
10960     /*
10961      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10962      */
10963     preferredTabWidth : 175,
10964     /*
10965      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10966      */
10967     resizeTabs : false,
10968     /*
10969      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10970      */
10971     monitorResize : true,
10972     /*
10973      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10974      */
10975     toolbar : false,
10976
10977     /**
10978      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10979      * @param {String} id The id of the div to use <b>or create</b>
10980      * @param {String} text The text for the tab
10981      * @param {String} content (optional) Content to put in the TabPanelItem body
10982      * @param {Boolean} closable (optional) True to create a close icon on the tab
10983      * @return {Roo.TabPanelItem} The created TabPanelItem
10984      */
10985     addTab : function(id, text, content, closable){
10986         var item = new Roo.TabPanelItem(this, id, text, closable);
10987         this.addTabItem(item);
10988         if(content){
10989             item.setContent(content);
10990         }
10991         return item;
10992     },
10993
10994     /**
10995      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10996      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10997      * @return {Roo.TabPanelItem}
10998      */
10999     getTab : function(id){
11000         return this.items[id];
11001     },
11002
11003     /**
11004      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11005      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11006      */
11007     hideTab : function(id){
11008         var t = this.items[id];
11009         if(!t.isHidden()){
11010            t.setHidden(true);
11011            this.hiddenCount++;
11012            this.autoSizeTabs();
11013         }
11014     },
11015
11016     /**
11017      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11018      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11019      */
11020     unhideTab : function(id){
11021         var t = this.items[id];
11022         if(t.isHidden()){
11023            t.setHidden(false);
11024            this.hiddenCount--;
11025            this.autoSizeTabs();
11026         }
11027     },
11028
11029     /**
11030      * Adds an existing {@link Roo.TabPanelItem}.
11031      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11032      */
11033     addTabItem : function(item){
11034         this.items[item.id] = item;
11035         this.items.push(item);
11036         if(this.resizeTabs){
11037            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11038            this.autoSizeTabs();
11039         }else{
11040             item.autoSize();
11041         }
11042     },
11043
11044     /**
11045      * Removes a {@link Roo.TabPanelItem}.
11046      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11047      */
11048     removeTab : function(id){
11049         var items = this.items;
11050         var tab = items[id];
11051         if(!tab) { return; }
11052         var index = items.indexOf(tab);
11053         if(this.active == tab && items.length > 1){
11054             var newTab = this.getNextAvailable(index);
11055             if(newTab) {
11056                 newTab.activate();
11057             }
11058         }
11059         this.stripEl.dom.removeChild(tab.pnode.dom);
11060         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11061             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11062         }
11063         items.splice(index, 1);
11064         delete this.items[tab.id];
11065         tab.fireEvent("close", tab);
11066         tab.purgeListeners();
11067         this.autoSizeTabs();
11068     },
11069
11070     getNextAvailable : function(start){
11071         var items = this.items;
11072         var index = start;
11073         // look for a next tab that will slide over to
11074         // replace the one being removed
11075         while(index < items.length){
11076             var item = items[++index];
11077             if(item && !item.isHidden()){
11078                 return item;
11079             }
11080         }
11081         // if one isn't found select the previous tab (on the left)
11082         index = start;
11083         while(index >= 0){
11084             var item = items[--index];
11085             if(item && !item.isHidden()){
11086                 return item;
11087             }
11088         }
11089         return null;
11090     },
11091
11092     /**
11093      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11094      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11095      */
11096     disableTab : function(id){
11097         var tab = this.items[id];
11098         if(tab && this.active != tab){
11099             tab.disable();
11100         }
11101     },
11102
11103     /**
11104      * Enables a {@link Roo.TabPanelItem} that is disabled.
11105      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11106      */
11107     enableTab : function(id){
11108         var tab = this.items[id];
11109         tab.enable();
11110     },
11111
11112     /**
11113      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11114      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11115      * @return {Roo.TabPanelItem} The TabPanelItem.
11116      */
11117     activate : function(id){
11118         var tab = this.items[id];
11119         if(!tab){
11120             return null;
11121         }
11122         if(tab == this.active || tab.disabled){
11123             return tab;
11124         }
11125         var e = {};
11126         this.fireEvent("beforetabchange", this, e, tab);
11127         if(e.cancel !== true && !tab.disabled){
11128             if(this.active){
11129                 this.active.hide();
11130             }
11131             this.active = this.items[id];
11132             this.active.show();
11133             this.fireEvent("tabchange", this, this.active);
11134         }
11135         return tab;
11136     },
11137
11138     /**
11139      * Gets the active {@link Roo.TabPanelItem}.
11140      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11141      */
11142     getActiveTab : function(){
11143         return this.active;
11144     },
11145
11146     /**
11147      * Updates the tab body element to fit the height of the container element
11148      * for overflow scrolling
11149      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11150      */
11151     syncHeight : function(targetHeight){
11152         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11153         var bm = this.bodyEl.getMargins();
11154         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11155         this.bodyEl.setHeight(newHeight);
11156         return newHeight;
11157     },
11158
11159     onResize : function(){
11160         if(this.monitorResize){
11161             this.autoSizeTabs();
11162         }
11163     },
11164
11165     /**
11166      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11167      */
11168     beginUpdate : function(){
11169         this.updating = true;
11170     },
11171
11172     /**
11173      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11174      */
11175     endUpdate : function(){
11176         this.updating = false;
11177         this.autoSizeTabs();
11178     },
11179
11180     /**
11181      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11182      */
11183     autoSizeTabs : function(){
11184         var count = this.items.length;
11185         var vcount = count - this.hiddenCount;
11186         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11187         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11188         var availWidth = Math.floor(w / vcount);
11189         var b = this.stripBody;
11190         if(b.getWidth() > w){
11191             var tabs = this.items;
11192             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11193             if(availWidth < this.minTabWidth){
11194                 /*if(!this.sleft){    // incomplete scrolling code
11195                     this.createScrollButtons();
11196                 }
11197                 this.showScroll();
11198                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11199             }
11200         }else{
11201             if(this.currentTabWidth < this.preferredTabWidth){
11202                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11203             }
11204         }
11205     },
11206
11207     /**
11208      * Returns the number of tabs in this TabPanel.
11209      * @return {Number}
11210      */
11211      getCount : function(){
11212          return this.items.length;
11213      },
11214
11215     /**
11216      * Resizes all the tabs to the passed width
11217      * @param {Number} The new width
11218      */
11219     setTabWidth : function(width){
11220         this.currentTabWidth = width;
11221         for(var i = 0, len = this.items.length; i < len; i++) {
11222                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11223         }
11224     },
11225
11226     /**
11227      * Destroys this TabPanel
11228      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11229      */
11230     destroy : function(removeEl){
11231         Roo.EventManager.removeResizeListener(this.onResize, this);
11232         for(var i = 0, len = this.items.length; i < len; i++){
11233             this.items[i].purgeListeners();
11234         }
11235         if(removeEl === true){
11236             this.el.update("");
11237             this.el.remove();
11238         }
11239     }
11240 });
11241
11242 /**
11243  * @class Roo.TabPanelItem
11244  * @extends Roo.util.Observable
11245  * Represents an individual item (tab plus body) in a TabPanel.
11246  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11247  * @param {String} id The id of this TabPanelItem
11248  * @param {String} text The text for the tab of this TabPanelItem
11249  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11250  */
11251 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11252     /**
11253      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11254      * @type Roo.TabPanel
11255      */
11256     this.tabPanel = tabPanel;
11257     /**
11258      * The id for this TabPanelItem
11259      * @type String
11260      */
11261     this.id = id;
11262     /** @private */
11263     this.disabled = false;
11264     /** @private */
11265     this.text = text;
11266     /** @private */
11267     this.loaded = false;
11268     this.closable = closable;
11269
11270     /**
11271      * The body element for this TabPanelItem.
11272      * @type Roo.Element
11273      */
11274     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11275     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11276     this.bodyEl.setStyle("display", "block");
11277     this.bodyEl.setStyle("zoom", "1");
11278     this.hideAction();
11279
11280     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11281     /** @private */
11282     this.el = Roo.get(els.el, true);
11283     this.inner = Roo.get(els.inner, true);
11284     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11285     this.pnode = Roo.get(els.el.parentNode, true);
11286     this.el.on("mousedown", this.onTabMouseDown, this);
11287     this.el.on("click", this.onTabClick, this);
11288     /** @private */
11289     if(closable){
11290         var c = Roo.get(els.close, true);
11291         c.dom.title = this.closeText;
11292         c.addClassOnOver("close-over");
11293         c.on("click", this.closeClick, this);
11294      }
11295
11296     this.addEvents({
11297          /**
11298          * @event activate
11299          * Fires when this tab becomes the active tab.
11300          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11301          * @param {Roo.TabPanelItem} this
11302          */
11303         "activate": true,
11304         /**
11305          * @event beforeclose
11306          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11307          * @param {Roo.TabPanelItem} this
11308          * @param {Object} e Set cancel to true on this object to cancel the close.
11309          */
11310         "beforeclose": true,
11311         /**
11312          * @event close
11313          * Fires when this tab is closed.
11314          * @param {Roo.TabPanelItem} this
11315          */
11316          "close": true,
11317         /**
11318          * @event deactivate
11319          * Fires when this tab is no longer the active tab.
11320          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11321          * @param {Roo.TabPanelItem} this
11322          */
11323          "deactivate" : true
11324     });
11325     this.hidden = false;
11326
11327     Roo.TabPanelItem.superclass.constructor.call(this);
11328 };
11329
11330 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11331     purgeListeners : function(){
11332        Roo.util.Observable.prototype.purgeListeners.call(this);
11333        this.el.removeAllListeners();
11334     },
11335     /**
11336      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11337      */
11338     show : function(){
11339         this.pnode.addClass("on");
11340         this.showAction();
11341         if(Roo.isOpera){
11342             this.tabPanel.stripWrap.repaint();
11343         }
11344         this.fireEvent("activate", this.tabPanel, this);
11345     },
11346
11347     /**
11348      * Returns true if this tab is the active tab.
11349      * @return {Boolean}
11350      */
11351     isActive : function(){
11352         return this.tabPanel.getActiveTab() == this;
11353     },
11354
11355     /**
11356      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11357      */
11358     hide : function(){
11359         this.pnode.removeClass("on");
11360         this.hideAction();
11361         this.fireEvent("deactivate", this.tabPanel, this);
11362     },
11363
11364     hideAction : function(){
11365         this.bodyEl.hide();
11366         this.bodyEl.setStyle("position", "absolute");
11367         this.bodyEl.setLeft("-20000px");
11368         this.bodyEl.setTop("-20000px");
11369     },
11370
11371     showAction : function(){
11372         this.bodyEl.setStyle("position", "relative");
11373         this.bodyEl.setTop("");
11374         this.bodyEl.setLeft("");
11375         this.bodyEl.show();
11376     },
11377
11378     /**
11379      * Set the tooltip for the tab.
11380      * @param {String} tooltip The tab's tooltip
11381      */
11382     setTooltip : function(text){
11383         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11384             this.textEl.dom.qtip = text;
11385             this.textEl.dom.removeAttribute('title');
11386         }else{
11387             this.textEl.dom.title = text;
11388         }
11389     },
11390
11391     onTabClick : function(e){
11392         e.preventDefault();
11393         this.tabPanel.activate(this.id);
11394     },
11395
11396     onTabMouseDown : function(e){
11397         e.preventDefault();
11398         this.tabPanel.activate(this.id);
11399     },
11400
11401     getWidth : function(){
11402         return this.inner.getWidth();
11403     },
11404
11405     setWidth : function(width){
11406         var iwidth = width - this.pnode.getPadding("lr");
11407         this.inner.setWidth(iwidth);
11408         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11409         this.pnode.setWidth(width);
11410     },
11411
11412     /**
11413      * Show or hide the tab
11414      * @param {Boolean} hidden True to hide or false to show.
11415      */
11416     setHidden : function(hidden){
11417         this.hidden = hidden;
11418         this.pnode.setStyle("display", hidden ? "none" : "");
11419     },
11420
11421     /**
11422      * Returns true if this tab is "hidden"
11423      * @return {Boolean}
11424      */
11425     isHidden : function(){
11426         return this.hidden;
11427     },
11428
11429     /**
11430      * Returns the text for this tab
11431      * @return {String}
11432      */
11433     getText : function(){
11434         return this.text;
11435     },
11436
11437     autoSize : function(){
11438         //this.el.beginMeasure();
11439         this.textEl.setWidth(1);
11440         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11441         //this.el.endMeasure();
11442     },
11443
11444     /**
11445      * Sets the text for the tab (Note: this also sets the tooltip text)
11446      * @param {String} text The tab's text and tooltip
11447      */
11448     setText : function(text){
11449         this.text = text;
11450         this.textEl.update(text);
11451         this.setTooltip(text);
11452         if(!this.tabPanel.resizeTabs){
11453             this.autoSize();
11454         }
11455     },
11456     /**
11457      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11458      */
11459     activate : function(){
11460         this.tabPanel.activate(this.id);
11461     },
11462
11463     /**
11464      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11465      */
11466     disable : function(){
11467         if(this.tabPanel.active != this){
11468             this.disabled = true;
11469             this.pnode.addClass("disabled");
11470         }
11471     },
11472
11473     /**
11474      * Enables this TabPanelItem if it was previously disabled.
11475      */
11476     enable : function(){
11477         this.disabled = false;
11478         this.pnode.removeClass("disabled");
11479     },
11480
11481     /**
11482      * Sets the content for this TabPanelItem.
11483      * @param {String} content The content
11484      * @param {Boolean} loadScripts true to look for and load scripts
11485      */
11486     setContent : function(content, loadScripts){
11487         this.bodyEl.update(content, loadScripts);
11488     },
11489
11490     /**
11491      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11492      * @return {Roo.UpdateManager} The UpdateManager
11493      */
11494     getUpdateManager : function(){
11495         return this.bodyEl.getUpdateManager();
11496     },
11497
11498     /**
11499      * Set a URL to be used to load the content for this TabPanelItem.
11500      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11501      * @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)
11502      * @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)
11503      * @return {Roo.UpdateManager} The UpdateManager
11504      */
11505     setUrl : function(url, params, loadOnce){
11506         if(this.refreshDelegate){
11507             this.un('activate', this.refreshDelegate);
11508         }
11509         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11510         this.on("activate", this.refreshDelegate);
11511         return this.bodyEl.getUpdateManager();
11512     },
11513
11514     /** @private */
11515     _handleRefresh : function(url, params, loadOnce){
11516         if(!loadOnce || !this.loaded){
11517             var updater = this.bodyEl.getUpdateManager();
11518             updater.update(url, params, this._setLoaded.createDelegate(this));
11519         }
11520     },
11521
11522     /**
11523      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11524      *   Will fail silently if the setUrl method has not been called.
11525      *   This does not activate the panel, just updates its content.
11526      */
11527     refresh : function(){
11528         if(this.refreshDelegate){
11529            this.loaded = false;
11530            this.refreshDelegate();
11531         }
11532     },
11533
11534     /** @private */
11535     _setLoaded : function(){
11536         this.loaded = true;
11537     },
11538
11539     /** @private */
11540     closeClick : function(e){
11541         var o = {};
11542         e.stopEvent();
11543         this.fireEvent("beforeclose", this, o);
11544         if(o.cancel !== true){
11545             this.tabPanel.removeTab(this.id);
11546         }
11547     },
11548     /**
11549      * The text displayed in the tooltip for the close icon.
11550      * @type String
11551      */
11552     closeText : "Close this tab"
11553 });
11554
11555 /** @private */
11556 Roo.TabPanel.prototype.createStrip = function(container){
11557     var strip = document.createElement("div");
11558     strip.className = "x-tabs-wrap";
11559     container.appendChild(strip);
11560     return strip;
11561 };
11562 /** @private */
11563 Roo.TabPanel.prototype.createStripList = function(strip){
11564     // div wrapper for retard IE
11565     // returns the "tr" element.
11566     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11567         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11568         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11569     return strip.firstChild.firstChild.firstChild.firstChild;
11570 };
11571 /** @private */
11572 Roo.TabPanel.prototype.createBody = function(container){
11573     var body = document.createElement("div");
11574     Roo.id(body, "tab-body");
11575     Roo.fly(body).addClass("x-tabs-body");
11576     container.appendChild(body);
11577     return body;
11578 };
11579 /** @private */
11580 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11581     var body = Roo.getDom(id);
11582     if(!body){
11583         body = document.createElement("div");
11584         body.id = id;
11585     }
11586     Roo.fly(body).addClass("x-tabs-item-body");
11587     bodyEl.insertBefore(body, bodyEl.firstChild);
11588     return body;
11589 };
11590 /** @private */
11591 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11592     var td = document.createElement("td");
11593     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11594     //stripEl.appendChild(td);
11595     if(closable){
11596         td.className = "x-tabs-closable";
11597         if(!this.closeTpl){
11598             this.closeTpl = new Roo.Template(
11599                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11600                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11601                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11602             );
11603         }
11604         var el = this.closeTpl.overwrite(td, {"text": text});
11605         var close = el.getElementsByTagName("div")[0];
11606         var inner = el.getElementsByTagName("em")[0];
11607         return {"el": el, "close": close, "inner": inner};
11608     } else {
11609         if(!this.tabTpl){
11610             this.tabTpl = new Roo.Template(
11611                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11612                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11613             );
11614         }
11615         var el = this.tabTpl.overwrite(td, {"text": text});
11616         var inner = el.getElementsByTagName("em")[0];
11617         return {"el": el, "inner": inner};
11618     }
11619 };/*
11620  * Based on:
11621  * Ext JS Library 1.1.1
11622  * Copyright(c) 2006-2007, Ext JS, LLC.
11623  *
11624  * Originally Released Under LGPL - original licence link has changed is not relivant.
11625  *
11626  * Fork - LGPL
11627  * <script type="text/javascript">
11628  */
11629
11630 /**
11631  * @class Roo.Button
11632  * @extends Roo.util.Observable
11633  * Simple Button class
11634  * @cfg {String} text The button text
11635  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11636  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11637  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11638  * @cfg {Object} scope The scope of the handler
11639  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11640  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11641  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11642  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11643  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11644  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11645    applies if enableToggle = true)
11646  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11647  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11648   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11649  * @constructor
11650  * Create a new button
11651  * @param {Object} config The config object
11652  */
11653 Roo.Button = function(renderTo, config)
11654 {
11655     if (!config) {
11656         config = renderTo;
11657         renderTo = config.renderTo || false;
11658     }
11659     
11660     Roo.apply(this, config);
11661     this.addEvents({
11662         /**
11663              * @event click
11664              * Fires when this button is clicked
11665              * @param {Button} this
11666              * @param {EventObject} e The click event
11667              */
11668             "click" : true,
11669         /**
11670              * @event toggle
11671              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11672              * @param {Button} this
11673              * @param {Boolean} pressed
11674              */
11675             "toggle" : true,
11676         /**
11677              * @event mouseover
11678              * Fires when the mouse hovers over the button
11679              * @param {Button} this
11680              * @param {Event} e The event object
11681              */
11682         'mouseover' : true,
11683         /**
11684              * @event mouseout
11685              * Fires when the mouse exits the button
11686              * @param {Button} this
11687              * @param {Event} e The event object
11688              */
11689         'mouseout': true,
11690          /**
11691              * @event render
11692              * Fires when the button is rendered
11693              * @param {Button} this
11694              */
11695         'render': true
11696     });
11697     if(this.menu){
11698         this.menu = Roo.menu.MenuMgr.get(this.menu);
11699     }
11700     // register listeners first!!  - so render can be captured..
11701     Roo.util.Observable.call(this);
11702     if(renderTo){
11703         this.render(renderTo);
11704     }
11705     
11706   
11707 };
11708
11709 Roo.extend(Roo.Button, Roo.util.Observable, {
11710     /**
11711      * 
11712      */
11713     
11714     /**
11715      * Read-only. True if this button is hidden
11716      * @type Boolean
11717      */
11718     hidden : false,
11719     /**
11720      * Read-only. True if this button is disabled
11721      * @type Boolean
11722      */
11723     disabled : false,
11724     /**
11725      * Read-only. True if this button is pressed (only if enableToggle = true)
11726      * @type Boolean
11727      */
11728     pressed : false,
11729
11730     /**
11731      * @cfg {Number} tabIndex 
11732      * The DOM tabIndex for this button (defaults to undefined)
11733      */
11734     tabIndex : undefined,
11735
11736     /**
11737      * @cfg {Boolean} enableToggle
11738      * True to enable pressed/not pressed toggling (defaults to false)
11739      */
11740     enableToggle: false,
11741     /**
11742      * @cfg {Mixed} menu
11743      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11744      */
11745     menu : undefined,
11746     /**
11747      * @cfg {String} menuAlign
11748      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11749      */
11750     menuAlign : "tl-bl?",
11751
11752     /**
11753      * @cfg {String} iconCls
11754      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11755      */
11756     iconCls : undefined,
11757     /**
11758      * @cfg {String} type
11759      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11760      */
11761     type : 'button',
11762
11763     // private
11764     menuClassTarget: 'tr',
11765
11766     /**
11767      * @cfg {String} clickEvent
11768      * The type of event to map to the button's event handler (defaults to 'click')
11769      */
11770     clickEvent : 'click',
11771
11772     /**
11773      * @cfg {Boolean} handleMouseEvents
11774      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11775      */
11776     handleMouseEvents : true,
11777
11778     /**
11779      * @cfg {String} tooltipType
11780      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11781      */
11782     tooltipType : 'qtip',
11783
11784     /**
11785      * @cfg {String} cls
11786      * A CSS class to apply to the button's main element.
11787      */
11788     
11789     /**
11790      * @cfg {Roo.Template} template (Optional)
11791      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11792      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11793      * require code modifications if required elements (e.g. a button) aren't present.
11794      */
11795
11796     // private
11797     render : function(renderTo){
11798         var btn;
11799         if(this.hideParent){
11800             this.parentEl = Roo.get(renderTo);
11801         }
11802         if(!this.dhconfig){
11803             if(!this.template){
11804                 if(!Roo.Button.buttonTemplate){
11805                     // hideous table template
11806                     Roo.Button.buttonTemplate = new Roo.Template(
11807                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11808                         '<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>',
11809                         "</tr></tbody></table>");
11810                 }
11811                 this.template = Roo.Button.buttonTemplate;
11812             }
11813             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11814             var btnEl = btn.child("button:first");
11815             btnEl.on('focus', this.onFocus, this);
11816             btnEl.on('blur', this.onBlur, this);
11817             if(this.cls){
11818                 btn.addClass(this.cls);
11819             }
11820             if(this.icon){
11821                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11822             }
11823             if(this.iconCls){
11824                 btnEl.addClass(this.iconCls);
11825                 if(!this.cls){
11826                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11827                 }
11828             }
11829             if(this.tabIndex !== undefined){
11830                 btnEl.dom.tabIndex = this.tabIndex;
11831             }
11832             if(this.tooltip){
11833                 if(typeof this.tooltip == 'object'){
11834                     Roo.QuickTips.tips(Roo.apply({
11835                           target: btnEl.id
11836                     }, this.tooltip));
11837                 } else {
11838                     btnEl.dom[this.tooltipType] = this.tooltip;
11839                 }
11840             }
11841         }else{
11842             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11843         }
11844         this.el = btn;
11845         if(this.id){
11846             this.el.dom.id = this.el.id = this.id;
11847         }
11848         if(this.menu){
11849             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11850             this.menu.on("show", this.onMenuShow, this);
11851             this.menu.on("hide", this.onMenuHide, this);
11852         }
11853         btn.addClass("x-btn");
11854         if(Roo.isIE && !Roo.isIE7){
11855             this.autoWidth.defer(1, this);
11856         }else{
11857             this.autoWidth();
11858         }
11859         if(this.handleMouseEvents){
11860             btn.on("mouseover", this.onMouseOver, this);
11861             btn.on("mouseout", this.onMouseOut, this);
11862             btn.on("mousedown", this.onMouseDown, this);
11863         }
11864         btn.on(this.clickEvent, this.onClick, this);
11865         //btn.on("mouseup", this.onMouseUp, this);
11866         if(this.hidden){
11867             this.hide();
11868         }
11869         if(this.disabled){
11870             this.disable();
11871         }
11872         Roo.ButtonToggleMgr.register(this);
11873         if(this.pressed){
11874             this.el.addClass("x-btn-pressed");
11875         }
11876         if(this.repeat){
11877             var repeater = new Roo.util.ClickRepeater(btn,
11878                 typeof this.repeat == "object" ? this.repeat : {}
11879             );
11880             repeater.on("click", this.onClick,  this);
11881         }
11882         
11883         this.fireEvent('render', this);
11884         
11885     },
11886     /**
11887      * Returns the button's underlying element
11888      * @return {Roo.Element} The element
11889      */
11890     getEl : function(){
11891         return this.el;  
11892     },
11893     
11894     /**
11895      * Destroys this Button and removes any listeners.
11896      */
11897     destroy : function(){
11898         Roo.ButtonToggleMgr.unregister(this);
11899         this.el.removeAllListeners();
11900         this.purgeListeners();
11901         this.el.remove();
11902     },
11903
11904     // private
11905     autoWidth : function(){
11906         if(this.el){
11907             this.el.setWidth("auto");
11908             if(Roo.isIE7 && Roo.isStrict){
11909                 var ib = this.el.child('button');
11910                 if(ib && ib.getWidth() > 20){
11911                     ib.clip();
11912                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11913                 }
11914             }
11915             if(this.minWidth){
11916                 if(this.hidden){
11917                     this.el.beginMeasure();
11918                 }
11919                 if(this.el.getWidth() < this.minWidth){
11920                     this.el.setWidth(this.minWidth);
11921                 }
11922                 if(this.hidden){
11923                     this.el.endMeasure();
11924                 }
11925             }
11926         }
11927     },
11928
11929     /**
11930      * Assigns this button's click handler
11931      * @param {Function} handler The function to call when the button is clicked
11932      * @param {Object} scope (optional) Scope for the function passed in
11933      */
11934     setHandler : function(handler, scope){
11935         this.handler = handler;
11936         this.scope = scope;  
11937     },
11938     
11939     /**
11940      * Sets this button's text
11941      * @param {String} text The button text
11942      */
11943     setText : function(text){
11944         this.text = text;
11945         if(this.el){
11946             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11947         }
11948         this.autoWidth();
11949     },
11950     
11951     /**
11952      * Gets the text for this button
11953      * @return {String} The button text
11954      */
11955     getText : function(){
11956         return this.text;  
11957     },
11958     
11959     /**
11960      * Show this button
11961      */
11962     show: function(){
11963         this.hidden = false;
11964         if(this.el){
11965             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11966         }
11967     },
11968     
11969     /**
11970      * Hide this button
11971      */
11972     hide: function(){
11973         this.hidden = true;
11974         if(this.el){
11975             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11976         }
11977     },
11978     
11979     /**
11980      * Convenience function for boolean show/hide
11981      * @param {Boolean} visible True to show, false to hide
11982      */
11983     setVisible: function(visible){
11984         if(visible) {
11985             this.show();
11986         }else{
11987             this.hide();
11988         }
11989     },
11990     
11991     /**
11992      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11993      * @param {Boolean} state (optional) Force a particular state
11994      */
11995     toggle : function(state){
11996         state = state === undefined ? !this.pressed : state;
11997         if(state != this.pressed){
11998             if(state){
11999                 this.el.addClass("x-btn-pressed");
12000                 this.pressed = true;
12001                 this.fireEvent("toggle", this, true);
12002             }else{
12003                 this.el.removeClass("x-btn-pressed");
12004                 this.pressed = false;
12005                 this.fireEvent("toggle", this, false);
12006             }
12007             if(this.toggleHandler){
12008                 this.toggleHandler.call(this.scope || this, this, state);
12009             }
12010         }
12011     },
12012     
12013     /**
12014      * Focus the button
12015      */
12016     focus : function(){
12017         this.el.child('button:first').focus();
12018     },
12019     
12020     /**
12021      * Disable this button
12022      */
12023     disable : function(){
12024         if(this.el){
12025             this.el.addClass("x-btn-disabled");
12026         }
12027         this.disabled = true;
12028     },
12029     
12030     /**
12031      * Enable this button
12032      */
12033     enable : function(){
12034         if(this.el){
12035             this.el.removeClass("x-btn-disabled");
12036         }
12037         this.disabled = false;
12038     },
12039
12040     /**
12041      * Convenience function for boolean enable/disable
12042      * @param {Boolean} enabled True to enable, false to disable
12043      */
12044     setDisabled : function(v){
12045         this[v !== true ? "enable" : "disable"]();
12046     },
12047
12048     // private
12049     onClick : function(e){
12050         if(e){
12051             e.preventDefault();
12052         }
12053         if(e.button != 0){
12054             return;
12055         }
12056         if(!this.disabled){
12057             if(this.enableToggle){
12058                 this.toggle();
12059             }
12060             if(this.menu && !this.menu.isVisible()){
12061                 this.menu.show(this.el, this.menuAlign);
12062             }
12063             this.fireEvent("click", this, e);
12064             if(this.handler){
12065                 this.el.removeClass("x-btn-over");
12066                 this.handler.call(this.scope || this, this, e);
12067             }
12068         }
12069     },
12070     // private
12071     onMouseOver : function(e){
12072         if(!this.disabled){
12073             this.el.addClass("x-btn-over");
12074             this.fireEvent('mouseover', this, e);
12075         }
12076     },
12077     // private
12078     onMouseOut : function(e){
12079         if(!e.within(this.el,  true)){
12080             this.el.removeClass("x-btn-over");
12081             this.fireEvent('mouseout', this, e);
12082         }
12083     },
12084     // private
12085     onFocus : function(e){
12086         if(!this.disabled){
12087             this.el.addClass("x-btn-focus");
12088         }
12089     },
12090     // private
12091     onBlur : function(e){
12092         this.el.removeClass("x-btn-focus");
12093     },
12094     // private
12095     onMouseDown : function(e){
12096         if(!this.disabled && e.button == 0){
12097             this.el.addClass("x-btn-click");
12098             Roo.get(document).on('mouseup', this.onMouseUp, this);
12099         }
12100     },
12101     // private
12102     onMouseUp : function(e){
12103         if(e.button == 0){
12104             this.el.removeClass("x-btn-click");
12105             Roo.get(document).un('mouseup', this.onMouseUp, this);
12106         }
12107     },
12108     // private
12109     onMenuShow : function(e){
12110         this.el.addClass("x-btn-menu-active");
12111     },
12112     // private
12113     onMenuHide : function(e){
12114         this.el.removeClass("x-btn-menu-active");
12115     }   
12116 });
12117
12118 // Private utility class used by Button
12119 Roo.ButtonToggleMgr = function(){
12120    var groups = {};
12121    
12122    function toggleGroup(btn, state){
12123        if(state){
12124            var g = groups[btn.toggleGroup];
12125            for(var i = 0, l = g.length; i < l; i++){
12126                if(g[i] != btn){
12127                    g[i].toggle(false);
12128                }
12129            }
12130        }
12131    }
12132    
12133    return {
12134        register : function(btn){
12135            if(!btn.toggleGroup){
12136                return;
12137            }
12138            var g = groups[btn.toggleGroup];
12139            if(!g){
12140                g = groups[btn.toggleGroup] = [];
12141            }
12142            g.push(btn);
12143            btn.on("toggle", toggleGroup);
12144        },
12145        
12146        unregister : function(btn){
12147            if(!btn.toggleGroup){
12148                return;
12149            }
12150            var g = groups[btn.toggleGroup];
12151            if(g){
12152                g.remove(btn);
12153                btn.un("toggle", toggleGroup);
12154            }
12155        }
12156    };
12157 }();/*
12158  * Based on:
12159  * Ext JS Library 1.1.1
12160  * Copyright(c) 2006-2007, Ext JS, LLC.
12161  *
12162  * Originally Released Under LGPL - original licence link has changed is not relivant.
12163  *
12164  * Fork - LGPL
12165  * <script type="text/javascript">
12166  */
12167  
12168 /**
12169  * @class Roo.SplitButton
12170  * @extends Roo.Button
12171  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12172  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12173  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12174  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12175  * @cfg {String} arrowTooltip The title attribute of the arrow
12176  * @constructor
12177  * Create a new menu button
12178  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12179  * @param {Object} config The config object
12180  */
12181 Roo.SplitButton = function(renderTo, config){
12182     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12183     /**
12184      * @event arrowclick
12185      * Fires when this button's arrow is clicked
12186      * @param {SplitButton} this
12187      * @param {EventObject} e The click event
12188      */
12189     this.addEvents({"arrowclick":true});
12190 };
12191
12192 Roo.extend(Roo.SplitButton, Roo.Button, {
12193     render : function(renderTo){
12194         // this is one sweet looking template!
12195         var tpl = new Roo.Template(
12196             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12197             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12198             '<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>',
12199             "</tbody></table></td><td>",
12200             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12201             '<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>',
12202             "</tbody></table></td></tr></table>"
12203         );
12204         var btn = tpl.append(renderTo, [this.text, this.type], true);
12205         var btnEl = btn.child("button");
12206         if(this.cls){
12207             btn.addClass(this.cls);
12208         }
12209         if(this.icon){
12210             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12211         }
12212         if(this.iconCls){
12213             btnEl.addClass(this.iconCls);
12214             if(!this.cls){
12215                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12216             }
12217         }
12218         this.el = btn;
12219         if(this.handleMouseEvents){
12220             btn.on("mouseover", this.onMouseOver, this);
12221             btn.on("mouseout", this.onMouseOut, this);
12222             btn.on("mousedown", this.onMouseDown, this);
12223             btn.on("mouseup", this.onMouseUp, this);
12224         }
12225         btn.on(this.clickEvent, this.onClick, this);
12226         if(this.tooltip){
12227             if(typeof this.tooltip == 'object'){
12228                 Roo.QuickTips.tips(Roo.apply({
12229                       target: btnEl.id
12230                 }, this.tooltip));
12231             } else {
12232                 btnEl.dom[this.tooltipType] = this.tooltip;
12233             }
12234         }
12235         if(this.arrowTooltip){
12236             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12237         }
12238         if(this.hidden){
12239             this.hide();
12240         }
12241         if(this.disabled){
12242             this.disable();
12243         }
12244         if(this.pressed){
12245             this.el.addClass("x-btn-pressed");
12246         }
12247         if(Roo.isIE && !Roo.isIE7){
12248             this.autoWidth.defer(1, this);
12249         }else{
12250             this.autoWidth();
12251         }
12252         if(this.menu){
12253             this.menu.on("show", this.onMenuShow, this);
12254             this.menu.on("hide", this.onMenuHide, this);
12255         }
12256         this.fireEvent('render', this);
12257     },
12258
12259     // private
12260     autoWidth : function(){
12261         if(this.el){
12262             var tbl = this.el.child("table:first");
12263             var tbl2 = this.el.child("table:last");
12264             this.el.setWidth("auto");
12265             tbl.setWidth("auto");
12266             if(Roo.isIE7 && Roo.isStrict){
12267                 var ib = this.el.child('button:first');
12268                 if(ib && ib.getWidth() > 20){
12269                     ib.clip();
12270                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12271                 }
12272             }
12273             if(this.minWidth){
12274                 if(this.hidden){
12275                     this.el.beginMeasure();
12276                 }
12277                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12278                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12279                 }
12280                 if(this.hidden){
12281                     this.el.endMeasure();
12282                 }
12283             }
12284             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12285         } 
12286     },
12287     /**
12288      * Sets this button's click handler
12289      * @param {Function} handler The function to call when the button is clicked
12290      * @param {Object} scope (optional) Scope for the function passed above
12291      */
12292     setHandler : function(handler, scope){
12293         this.handler = handler;
12294         this.scope = scope;  
12295     },
12296     
12297     /**
12298      * Sets this button's arrow click handler
12299      * @param {Function} handler The function to call when the arrow is clicked
12300      * @param {Object} scope (optional) Scope for the function passed above
12301      */
12302     setArrowHandler : function(handler, scope){
12303         this.arrowHandler = handler;
12304         this.scope = scope;  
12305     },
12306     
12307     /**
12308      * Focus the button
12309      */
12310     focus : function(){
12311         if(this.el){
12312             this.el.child("button:first").focus();
12313         }
12314     },
12315
12316     // private
12317     onClick : function(e){
12318         e.preventDefault();
12319         if(!this.disabled){
12320             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12321                 if(this.menu && !this.menu.isVisible()){
12322                     this.menu.show(this.el, this.menuAlign);
12323                 }
12324                 this.fireEvent("arrowclick", this, e);
12325                 if(this.arrowHandler){
12326                     this.arrowHandler.call(this.scope || this, this, e);
12327                 }
12328             }else{
12329                 this.fireEvent("click", this, e);
12330                 if(this.handler){
12331                     this.handler.call(this.scope || this, this, e);
12332                 }
12333             }
12334         }
12335     },
12336     // private
12337     onMouseDown : function(e){
12338         if(!this.disabled){
12339             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12340         }
12341     },
12342     // private
12343     onMouseUp : function(e){
12344         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12345     }   
12346 });
12347
12348
12349 // backwards compat
12350 Roo.MenuButton = Roo.SplitButton;/*
12351  * Based on:
12352  * Ext JS Library 1.1.1
12353  * Copyright(c) 2006-2007, Ext JS, LLC.
12354  *
12355  * Originally Released Under LGPL - original licence link has changed is not relivant.
12356  *
12357  * Fork - LGPL
12358  * <script type="text/javascript">
12359  */
12360
12361 /**
12362  * @class Roo.Toolbar
12363  * Basic Toolbar class.
12364  * @constructor
12365  * Creates a new Toolbar
12366  * @param {Object} container The config object
12367  */ 
12368 Roo.Toolbar = function(container, buttons, config)
12369 {
12370     /// old consturctor format still supported..
12371     if(container instanceof Array){ // omit the container for later rendering
12372         buttons = container;
12373         config = buttons;
12374         container = null;
12375     }
12376     if (typeof(container) == 'object' && container.xtype) {
12377         config = container;
12378         container = config.container;
12379         buttons = config.buttons || []; // not really - use items!!
12380     }
12381     var xitems = [];
12382     if (config && config.items) {
12383         xitems = config.items;
12384         delete config.items;
12385     }
12386     Roo.apply(this, config);
12387     this.buttons = buttons;
12388     
12389     if(container){
12390         this.render(container);
12391     }
12392     this.xitems = xitems;
12393     Roo.each(xitems, function(b) {
12394         this.add(b);
12395     }, this);
12396     
12397 };
12398
12399 Roo.Toolbar.prototype = {
12400     /**
12401      * @cfg {Array} items
12402      * array of button configs or elements to add (will be converted to a MixedCollection)
12403      */
12404     
12405     /**
12406      * @cfg {String/HTMLElement/Element} container
12407      * The id or element that will contain the toolbar
12408      */
12409     // private
12410     render : function(ct){
12411         this.el = Roo.get(ct);
12412         if(this.cls){
12413             this.el.addClass(this.cls);
12414         }
12415         // using a table allows for vertical alignment
12416         // 100% width is needed by Safari...
12417         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12418         this.tr = this.el.child("tr", true);
12419         var autoId = 0;
12420         this.items = new Roo.util.MixedCollection(false, function(o){
12421             return o.id || ("item" + (++autoId));
12422         });
12423         if(this.buttons){
12424             this.add.apply(this, this.buttons);
12425             delete this.buttons;
12426         }
12427     },
12428
12429     /**
12430      * Adds element(s) to the toolbar -- this function takes a variable number of 
12431      * arguments of mixed type and adds them to the toolbar.
12432      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12433      * <ul>
12434      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12435      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12436      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12437      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12438      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12439      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12440      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12441      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12442      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12443      * </ul>
12444      * @param {Mixed} arg2
12445      * @param {Mixed} etc.
12446      */
12447     add : function(){
12448         var a = arguments, l = a.length;
12449         for(var i = 0; i < l; i++){
12450             this._add(a[i]);
12451         }
12452     },
12453     // private..
12454     _add : function(el) {
12455         
12456         if (el.xtype) {
12457             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12458         }
12459         
12460         if (el.applyTo){ // some kind of form field
12461             return this.addField(el);
12462         } 
12463         if (el.render){ // some kind of Toolbar.Item
12464             return this.addItem(el);
12465         }
12466         if (typeof el == "string"){ // string
12467             if(el == "separator" || el == "-"){
12468                 return this.addSeparator();
12469             }
12470             if (el == " "){
12471                 return this.addSpacer();
12472             }
12473             if(el == "->"){
12474                 return this.addFill();
12475             }
12476             return this.addText(el);
12477             
12478         }
12479         if(el.tagName){ // element
12480             return this.addElement(el);
12481         }
12482         if(typeof el == "object"){ // must be button config?
12483             return this.addButton(el);
12484         }
12485         // and now what?!?!
12486         return false;
12487         
12488     },
12489     
12490     /**
12491      * Add an Xtype element
12492      * @param {Object} xtype Xtype Object
12493      * @return {Object} created Object
12494      */
12495     addxtype : function(e){
12496         return this.add(e);  
12497     },
12498     
12499     /**
12500      * Returns the Element for this toolbar.
12501      * @return {Roo.Element}
12502      */
12503     getEl : function(){
12504         return this.el;  
12505     },
12506     
12507     /**
12508      * Adds a separator
12509      * @return {Roo.Toolbar.Item} The separator item
12510      */
12511     addSeparator : function(){
12512         return this.addItem(new Roo.Toolbar.Separator());
12513     },
12514
12515     /**
12516      * Adds a spacer element
12517      * @return {Roo.Toolbar.Spacer} The spacer item
12518      */
12519     addSpacer : function(){
12520         return this.addItem(new Roo.Toolbar.Spacer());
12521     },
12522
12523     /**
12524      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12525      * @return {Roo.Toolbar.Fill} The fill item
12526      */
12527     addFill : function(){
12528         return this.addItem(new Roo.Toolbar.Fill());
12529     },
12530
12531     /**
12532      * Adds any standard HTML element to the toolbar
12533      * @param {String/HTMLElement/Element} el The element or id of the element to add
12534      * @return {Roo.Toolbar.Item} The element's item
12535      */
12536     addElement : function(el){
12537         return this.addItem(new Roo.Toolbar.Item(el));
12538     },
12539     /**
12540      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12541      * @type Roo.util.MixedCollection  
12542      */
12543     items : false,
12544      
12545     /**
12546      * Adds any Toolbar.Item or subclass
12547      * @param {Roo.Toolbar.Item} item
12548      * @return {Roo.Toolbar.Item} The item
12549      */
12550     addItem : function(item){
12551         var td = this.nextBlock();
12552         item.render(td);
12553         this.items.add(item);
12554         return item;
12555     },
12556     
12557     /**
12558      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12559      * @param {Object/Array} config A button config or array of configs
12560      * @return {Roo.Toolbar.Button/Array}
12561      */
12562     addButton : function(config){
12563         if(config instanceof Array){
12564             var buttons = [];
12565             for(var i = 0, len = config.length; i < len; i++) {
12566                 buttons.push(this.addButton(config[i]));
12567             }
12568             return buttons;
12569         }
12570         var b = config;
12571         if(!(config instanceof Roo.Toolbar.Button)){
12572             b = config.split ?
12573                 new Roo.Toolbar.SplitButton(config) :
12574                 new Roo.Toolbar.Button(config);
12575         }
12576         var td = this.nextBlock();
12577         b.render(td);
12578         this.items.add(b);
12579         return b;
12580     },
12581     
12582     /**
12583      * Adds text to the toolbar
12584      * @param {String} text The text to add
12585      * @return {Roo.Toolbar.Item} The element's item
12586      */
12587     addText : function(text){
12588         return this.addItem(new Roo.Toolbar.TextItem(text));
12589     },
12590     
12591     /**
12592      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12593      * @param {Number} index The index where the item is to be inserted
12594      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12595      * @return {Roo.Toolbar.Button/Item}
12596      */
12597     insertButton : function(index, item){
12598         if(item instanceof Array){
12599             var buttons = [];
12600             for(var i = 0, len = item.length; i < len; i++) {
12601                buttons.push(this.insertButton(index + i, item[i]));
12602             }
12603             return buttons;
12604         }
12605         if (!(item instanceof Roo.Toolbar.Button)){
12606            item = new Roo.Toolbar.Button(item);
12607         }
12608         var td = document.createElement("td");
12609         this.tr.insertBefore(td, this.tr.childNodes[index]);
12610         item.render(td);
12611         this.items.insert(index, item);
12612         return item;
12613     },
12614     
12615     /**
12616      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12617      * @param {Object} config
12618      * @return {Roo.Toolbar.Item} The element's item
12619      */
12620     addDom : function(config, returnEl){
12621         var td = this.nextBlock();
12622         Roo.DomHelper.overwrite(td, config);
12623         var ti = new Roo.Toolbar.Item(td.firstChild);
12624         ti.render(td);
12625         this.items.add(ti);
12626         return ti;
12627     },
12628
12629     /**
12630      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12631      * @type Roo.util.MixedCollection  
12632      */
12633     fields : false,
12634     
12635     /**
12636      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12637      * Note: the field should not have been rendered yet. For a field that has already been
12638      * rendered, use {@link #addElement}.
12639      * @param {Roo.form.Field} field
12640      * @return {Roo.ToolbarItem}
12641      */
12642      
12643       
12644     addField : function(field) {
12645         if (!this.fields) {
12646             var autoId = 0;
12647             this.fields = new Roo.util.MixedCollection(false, function(o){
12648                 return o.id || ("item" + (++autoId));
12649             });
12650
12651         }
12652         
12653         var td = this.nextBlock();
12654         field.render(td);
12655         var ti = new Roo.Toolbar.Item(td.firstChild);
12656         ti.render(td);
12657         this.items.add(ti);
12658         this.fields.add(field);
12659         return ti;
12660     },
12661     /**
12662      * Hide the toolbar
12663      * @method hide
12664      */
12665      
12666       
12667     hide : function()
12668     {
12669         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12670         this.el.child('div').hide();
12671     },
12672     /**
12673      * Show the toolbar
12674      * @method show
12675      */
12676     show : function()
12677     {
12678         this.el.child('div').show();
12679     },
12680       
12681     // private
12682     nextBlock : function(){
12683         var td = document.createElement("td");
12684         this.tr.appendChild(td);
12685         return td;
12686     },
12687
12688     // private
12689     destroy : function(){
12690         if(this.items){ // rendered?
12691             Roo.destroy.apply(Roo, this.items.items);
12692         }
12693         if(this.fields){ // rendered?
12694             Roo.destroy.apply(Roo, this.fields.items);
12695         }
12696         Roo.Element.uncache(this.el, this.tr);
12697     }
12698 };
12699
12700 /**
12701  * @class Roo.Toolbar.Item
12702  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12703  * @constructor
12704  * Creates a new Item
12705  * @param {HTMLElement} el 
12706  */
12707 Roo.Toolbar.Item = function(el){
12708     this.el = Roo.getDom(el);
12709     this.id = Roo.id(this.el);
12710     this.hidden = false;
12711 };
12712
12713 Roo.Toolbar.Item.prototype = {
12714     
12715     /**
12716      * Get this item's HTML Element
12717      * @return {HTMLElement}
12718      */
12719     getEl : function(){
12720        return this.el;  
12721     },
12722
12723     // private
12724     render : function(td){
12725         this.td = td;
12726         td.appendChild(this.el);
12727     },
12728     
12729     /**
12730      * Removes and destroys this item.
12731      */
12732     destroy : function(){
12733         this.td.parentNode.removeChild(this.td);
12734     },
12735     
12736     /**
12737      * Shows this item.
12738      */
12739     show: function(){
12740         this.hidden = false;
12741         this.td.style.display = "";
12742     },
12743     
12744     /**
12745      * Hides this item.
12746      */
12747     hide: function(){
12748         this.hidden = true;
12749         this.td.style.display = "none";
12750     },
12751     
12752     /**
12753      * Convenience function for boolean show/hide.
12754      * @param {Boolean} visible true to show/false to hide
12755      */
12756     setVisible: function(visible){
12757         if(visible) {
12758             this.show();
12759         }else{
12760             this.hide();
12761         }
12762     },
12763     
12764     /**
12765      * Try to focus this item.
12766      */
12767     focus : function(){
12768         Roo.fly(this.el).focus();
12769     },
12770     
12771     /**
12772      * Disables this item.
12773      */
12774     disable : function(){
12775         Roo.fly(this.td).addClass("x-item-disabled");
12776         this.disabled = true;
12777         this.el.disabled = true;
12778     },
12779     
12780     /**
12781      * Enables this item.
12782      */
12783     enable : function(){
12784         Roo.fly(this.td).removeClass("x-item-disabled");
12785         this.disabled = false;
12786         this.el.disabled = false;
12787     }
12788 };
12789
12790
12791 /**
12792  * @class Roo.Toolbar.Separator
12793  * @extends Roo.Toolbar.Item
12794  * A simple toolbar separator class
12795  * @constructor
12796  * Creates a new Separator
12797  */
12798 Roo.Toolbar.Separator = function(){
12799     var s = document.createElement("span");
12800     s.className = "ytb-sep";
12801     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12802 };
12803 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12804     enable:Roo.emptyFn,
12805     disable:Roo.emptyFn,
12806     focus:Roo.emptyFn
12807 });
12808
12809 /**
12810  * @class Roo.Toolbar.Spacer
12811  * @extends Roo.Toolbar.Item
12812  * A simple element that adds extra horizontal space to a toolbar.
12813  * @constructor
12814  * Creates a new Spacer
12815  */
12816 Roo.Toolbar.Spacer = function(){
12817     var s = document.createElement("div");
12818     s.className = "ytb-spacer";
12819     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12820 };
12821 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12822     enable:Roo.emptyFn,
12823     disable:Roo.emptyFn,
12824     focus:Roo.emptyFn
12825 });
12826
12827 /**
12828  * @class Roo.Toolbar.Fill
12829  * @extends Roo.Toolbar.Spacer
12830  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12831  * @constructor
12832  * Creates a new Spacer
12833  */
12834 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12835     // private
12836     render : function(td){
12837         td.style.width = '100%';
12838         Roo.Toolbar.Fill.superclass.render.call(this, td);
12839     }
12840 });
12841
12842 /**
12843  * @class Roo.Toolbar.TextItem
12844  * @extends Roo.Toolbar.Item
12845  * A simple class that renders text directly into a toolbar.
12846  * @constructor
12847  * Creates a new TextItem
12848  * @param {String} text
12849  */
12850 Roo.Toolbar.TextItem = function(text){
12851     if (typeof(text) == 'object') {
12852         text = text.text;
12853     }
12854     var s = document.createElement("span");
12855     s.className = "ytb-text";
12856     s.innerHTML = text;
12857     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12858 };
12859 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12860     enable:Roo.emptyFn,
12861     disable:Roo.emptyFn,
12862     focus:Roo.emptyFn
12863 });
12864
12865 /**
12866  * @class Roo.Toolbar.Button
12867  * @extends Roo.Button
12868  * A button that renders into a toolbar.
12869  * @constructor
12870  * Creates a new Button
12871  * @param {Object} config A standard {@link Roo.Button} config object
12872  */
12873 Roo.Toolbar.Button = function(config){
12874     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12875 };
12876 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12877     render : function(td){
12878         this.td = td;
12879         Roo.Toolbar.Button.superclass.render.call(this, td);
12880     },
12881     
12882     /**
12883      * Removes and destroys this button
12884      */
12885     destroy : function(){
12886         Roo.Toolbar.Button.superclass.destroy.call(this);
12887         this.td.parentNode.removeChild(this.td);
12888     },
12889     
12890     /**
12891      * Shows this button
12892      */
12893     show: function(){
12894         this.hidden = false;
12895         this.td.style.display = "";
12896     },
12897     
12898     /**
12899      * Hides this button
12900      */
12901     hide: function(){
12902         this.hidden = true;
12903         this.td.style.display = "none";
12904     },
12905
12906     /**
12907      * Disables this item
12908      */
12909     disable : function(){
12910         Roo.fly(this.td).addClass("x-item-disabled");
12911         this.disabled = true;
12912     },
12913
12914     /**
12915      * Enables this item
12916      */
12917     enable : function(){
12918         Roo.fly(this.td).removeClass("x-item-disabled");
12919         this.disabled = false;
12920     }
12921 });
12922 // backwards compat
12923 Roo.ToolbarButton = Roo.Toolbar.Button;
12924
12925 /**
12926  * @class Roo.Toolbar.SplitButton
12927  * @extends Roo.SplitButton
12928  * A menu button that renders into a toolbar.
12929  * @constructor
12930  * Creates a new SplitButton
12931  * @param {Object} config A standard {@link Roo.SplitButton} config object
12932  */
12933 Roo.Toolbar.SplitButton = function(config){
12934     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12935 };
12936 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12937     render : function(td){
12938         this.td = td;
12939         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12940     },
12941     
12942     /**
12943      * Removes and destroys this button
12944      */
12945     destroy : function(){
12946         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12947         this.td.parentNode.removeChild(this.td);
12948     },
12949     
12950     /**
12951      * Shows this button
12952      */
12953     show: function(){
12954         this.hidden = false;
12955         this.td.style.display = "";
12956     },
12957     
12958     /**
12959      * Hides this button
12960      */
12961     hide: function(){
12962         this.hidden = true;
12963         this.td.style.display = "none";
12964     }
12965 });
12966
12967 // backwards compat
12968 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12969  * Based on:
12970  * Ext JS Library 1.1.1
12971  * Copyright(c) 2006-2007, Ext JS, LLC.
12972  *
12973  * Originally Released Under LGPL - original licence link has changed is not relivant.
12974  *
12975  * Fork - LGPL
12976  * <script type="text/javascript">
12977  */
12978  
12979 /**
12980  * @class Roo.PagingToolbar
12981  * @extends Roo.Toolbar
12982  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12983  * @constructor
12984  * Create a new PagingToolbar
12985  * @param {Object} config The config object
12986  */
12987 Roo.PagingToolbar = function(el, ds, config)
12988 {
12989     // old args format still supported... - xtype is prefered..
12990     if (typeof(el) == 'object' && el.xtype) {
12991         // created from xtype...
12992         config = el;
12993         ds = el.dataSource;
12994         el = config.container;
12995     }
12996     var items = [];
12997     if (config.items) {
12998         items = config.items;
12999         config.items = [];
13000     }
13001     
13002     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13003     this.ds = ds;
13004     this.cursor = 0;
13005     this.renderButtons(this.el);
13006     this.bind(ds);
13007     
13008     // supprot items array.
13009    
13010     Roo.each(items, function(e) {
13011         this.add(Roo.factory(e));
13012     },this);
13013     
13014 };
13015
13016 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13017     /**
13018      * @cfg {Roo.data.Store} dataSource
13019      * The underlying data store providing the paged data
13020      */
13021     /**
13022      * @cfg {String/HTMLElement/Element} container
13023      * container The id or element that will contain the toolbar
13024      */
13025     /**
13026      * @cfg {Boolean} displayInfo
13027      * True to display the displayMsg (defaults to false)
13028      */
13029     /**
13030      * @cfg {Number} pageSize
13031      * The number of records to display per page (defaults to 20)
13032      */
13033     pageSize: 20,
13034     /**
13035      * @cfg {String} displayMsg
13036      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13037      */
13038     displayMsg : 'Displaying {0} - {1} of {2}',
13039     /**
13040      * @cfg {String} emptyMsg
13041      * The message to display when no records are found (defaults to "No data to display")
13042      */
13043     emptyMsg : 'No data to display',
13044     /**
13045      * Customizable piece of the default paging text (defaults to "Page")
13046      * @type String
13047      */
13048     beforePageText : "Page",
13049     /**
13050      * Customizable piece of the default paging text (defaults to "of %0")
13051      * @type String
13052      */
13053     afterPageText : "of {0}",
13054     /**
13055      * Customizable piece of the default paging text (defaults to "First Page")
13056      * @type String
13057      */
13058     firstText : "First Page",
13059     /**
13060      * Customizable piece of the default paging text (defaults to "Previous Page")
13061      * @type String
13062      */
13063     prevText : "Previous Page",
13064     /**
13065      * Customizable piece of the default paging text (defaults to "Next Page")
13066      * @type String
13067      */
13068     nextText : "Next Page",
13069     /**
13070      * Customizable piece of the default paging text (defaults to "Last Page")
13071      * @type String
13072      */
13073     lastText : "Last Page",
13074     /**
13075      * Customizable piece of the default paging text (defaults to "Refresh")
13076      * @type String
13077      */
13078     refreshText : "Refresh",
13079
13080     // private
13081     renderButtons : function(el){
13082         Roo.PagingToolbar.superclass.render.call(this, el);
13083         this.first = this.addButton({
13084             tooltip: this.firstText,
13085             cls: "x-btn-icon x-grid-page-first",
13086             disabled: true,
13087             handler: this.onClick.createDelegate(this, ["first"])
13088         });
13089         this.prev = this.addButton({
13090             tooltip: this.prevText,
13091             cls: "x-btn-icon x-grid-page-prev",
13092             disabled: true,
13093             handler: this.onClick.createDelegate(this, ["prev"])
13094         });
13095         //this.addSeparator();
13096         this.add(this.beforePageText);
13097         this.field = Roo.get(this.addDom({
13098            tag: "input",
13099            type: "text",
13100            size: "3",
13101            value: "1",
13102            cls: "x-grid-page-number"
13103         }).el);
13104         this.field.on("keydown", this.onPagingKeydown, this);
13105         this.field.on("focus", function(){this.dom.select();});
13106         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13107         this.field.setHeight(18);
13108         //this.addSeparator();
13109         this.next = this.addButton({
13110             tooltip: this.nextText,
13111             cls: "x-btn-icon x-grid-page-next",
13112             disabled: true,
13113             handler: this.onClick.createDelegate(this, ["next"])
13114         });
13115         this.last = this.addButton({
13116             tooltip: this.lastText,
13117             cls: "x-btn-icon x-grid-page-last",
13118             disabled: true,
13119             handler: this.onClick.createDelegate(this, ["last"])
13120         });
13121         //this.addSeparator();
13122         this.loading = this.addButton({
13123             tooltip: this.refreshText,
13124             cls: "x-btn-icon x-grid-loading",
13125             handler: this.onClick.createDelegate(this, ["refresh"])
13126         });
13127
13128         if(this.displayInfo){
13129             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13130         }
13131     },
13132
13133     // private
13134     updateInfo : function(){
13135         if(this.displayEl){
13136             var count = this.ds.getCount();
13137             var msg = count == 0 ?
13138                 this.emptyMsg :
13139                 String.format(
13140                     this.displayMsg,
13141                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13142                 );
13143             this.displayEl.update(msg);
13144         }
13145     },
13146
13147     // private
13148     onLoad : function(ds, r, o){
13149        this.cursor = o.params ? o.params.start : 0;
13150        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13151
13152        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13153        this.field.dom.value = ap;
13154        this.first.setDisabled(ap == 1);
13155        this.prev.setDisabled(ap == 1);
13156        this.next.setDisabled(ap == ps);
13157        this.last.setDisabled(ap == ps);
13158        this.loading.enable();
13159        this.updateInfo();
13160     },
13161
13162     // private
13163     getPageData : function(){
13164         var total = this.ds.getTotalCount();
13165         return {
13166             total : total,
13167             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13168             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13169         };
13170     },
13171
13172     // private
13173     onLoadError : function(){
13174         this.loading.enable();
13175     },
13176
13177     // private
13178     onPagingKeydown : function(e){
13179         var k = e.getKey();
13180         var d = this.getPageData();
13181         if(k == e.RETURN){
13182             var v = this.field.dom.value, pageNum;
13183             if(!v || isNaN(pageNum = parseInt(v, 10))){
13184                 this.field.dom.value = d.activePage;
13185                 return;
13186             }
13187             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13188             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13189             e.stopEvent();
13190         }
13191         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))
13192         {
13193           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13194           this.field.dom.value = pageNum;
13195           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13196           e.stopEvent();
13197         }
13198         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13199         {
13200           var v = this.field.dom.value, pageNum; 
13201           var increment = (e.shiftKey) ? 10 : 1;
13202           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13203             increment *= -1;
13204           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13205             this.field.dom.value = d.activePage;
13206             return;
13207           }
13208           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13209           {
13210             this.field.dom.value = parseInt(v, 10) + increment;
13211             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13212             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13213           }
13214           e.stopEvent();
13215         }
13216     },
13217
13218     // private
13219     beforeLoad : function(){
13220         if(this.loading){
13221             this.loading.disable();
13222         }
13223     },
13224
13225     // private
13226     onClick : function(which){
13227         var ds = this.ds;
13228         switch(which){
13229             case "first":
13230                 ds.load({params:{start: 0, limit: this.pageSize}});
13231             break;
13232             case "prev":
13233                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13234             break;
13235             case "next":
13236                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13237             break;
13238             case "last":
13239                 var total = ds.getTotalCount();
13240                 var extra = total % this.pageSize;
13241                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13242                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13243             break;
13244             case "refresh":
13245                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13246             break;
13247         }
13248     },
13249
13250     /**
13251      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13252      * @param {Roo.data.Store} store The data store to unbind
13253      */
13254     unbind : function(ds){
13255         ds.un("beforeload", this.beforeLoad, this);
13256         ds.un("load", this.onLoad, this);
13257         ds.un("loadexception", this.onLoadError, this);
13258         ds.un("remove", this.updateInfo, this);
13259         ds.un("add", this.updateInfo, this);
13260         this.ds = undefined;
13261     },
13262
13263     /**
13264      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13265      * @param {Roo.data.Store} store The data store to bind
13266      */
13267     bind : function(ds){
13268         ds.on("beforeload", this.beforeLoad, this);
13269         ds.on("load", this.onLoad, this);
13270         ds.on("loadexception", this.onLoadError, this);
13271         ds.on("remove", this.updateInfo, this);
13272         ds.on("add", this.updateInfo, this);
13273         this.ds = ds;
13274     }
13275 });/*
13276  * Based on:
13277  * Ext JS Library 1.1.1
13278  * Copyright(c) 2006-2007, Ext JS, LLC.
13279  *
13280  * Originally Released Under LGPL - original licence link has changed is not relivant.
13281  *
13282  * Fork - LGPL
13283  * <script type="text/javascript">
13284  */
13285
13286 /**
13287  * @class Roo.Resizable
13288  * @extends Roo.util.Observable
13289  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13290  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13291  * 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
13292  * the element will be wrapped for you automatically.</p>
13293  * <p>Here is the list of valid resize handles:</p>
13294  * <pre>
13295 Value   Description
13296 ------  -------------------
13297  'n'     north
13298  's'     south
13299  'e'     east
13300  'w'     west
13301  'nw'    northwest
13302  'sw'    southwest
13303  'se'    southeast
13304  'ne'    northeast
13305  'hd'    horizontal drag
13306  'all'   all
13307 </pre>
13308  * <p>Here's an example showing the creation of a typical Resizable:</p>
13309  * <pre><code>
13310 var resizer = new Roo.Resizable("element-id", {
13311     handles: 'all',
13312     minWidth: 200,
13313     minHeight: 100,
13314     maxWidth: 500,
13315     maxHeight: 400,
13316     pinned: true
13317 });
13318 resizer.on("resize", myHandler);
13319 </code></pre>
13320  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13321  * resizer.east.setDisplayed(false);</p>
13322  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13323  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13324  * resize operation's new size (defaults to [0, 0])
13325  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13326  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13327  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13328  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13329  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13330  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13331  * @cfg {Number} width The width of the element in pixels (defaults to null)
13332  * @cfg {Number} height The height of the element in pixels (defaults to null)
13333  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13334  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13335  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13336  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13337  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13338  * in favor of the handles config option (defaults to false)
13339  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13340  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13341  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13342  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13343  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13344  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13345  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13346  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13347  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13348  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13349  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13350  * @constructor
13351  * Create a new resizable component
13352  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13353  * @param {Object} config configuration options
13354   */
13355 Roo.Resizable = function(el, config)
13356 {
13357     this.el = Roo.get(el);
13358
13359     if(config && config.wrap){
13360         config.resizeChild = this.el;
13361         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13362         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13363         this.el.setStyle("overflow", "hidden");
13364         this.el.setPositioning(config.resizeChild.getPositioning());
13365         config.resizeChild.clearPositioning();
13366         if(!config.width || !config.height){
13367             var csize = config.resizeChild.getSize();
13368             this.el.setSize(csize.width, csize.height);
13369         }
13370         if(config.pinned && !config.adjustments){
13371             config.adjustments = "auto";
13372         }
13373     }
13374
13375     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13376     this.proxy.unselectable();
13377     this.proxy.enableDisplayMode('block');
13378
13379     Roo.apply(this, config);
13380
13381     if(this.pinned){
13382         this.disableTrackOver = true;
13383         this.el.addClass("x-resizable-pinned");
13384     }
13385     // if the element isn't positioned, make it relative
13386     var position = this.el.getStyle("position");
13387     if(position != "absolute" && position != "fixed"){
13388         this.el.setStyle("position", "relative");
13389     }
13390     if(!this.handles){ // no handles passed, must be legacy style
13391         this.handles = 's,e,se';
13392         if(this.multiDirectional){
13393             this.handles += ',n,w';
13394         }
13395     }
13396     if(this.handles == "all"){
13397         this.handles = "n s e w ne nw se sw";
13398     }
13399     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13400     var ps = Roo.Resizable.positions;
13401     for(var i = 0, len = hs.length; i < len; i++){
13402         if(hs[i] && ps[hs[i]]){
13403             var pos = ps[hs[i]];
13404             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13405         }
13406     }
13407     // legacy
13408     this.corner = this.southeast;
13409     
13410     // updateBox = the box can move..
13411     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13412         this.updateBox = true;
13413     }
13414
13415     this.activeHandle = null;
13416
13417     if(this.resizeChild){
13418         if(typeof this.resizeChild == "boolean"){
13419             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13420         }else{
13421             this.resizeChild = Roo.get(this.resizeChild, true);
13422         }
13423     }
13424     
13425     if(this.adjustments == "auto"){
13426         var rc = this.resizeChild;
13427         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13428         if(rc && (hw || hn)){
13429             rc.position("relative");
13430             rc.setLeft(hw ? hw.el.getWidth() : 0);
13431             rc.setTop(hn ? hn.el.getHeight() : 0);
13432         }
13433         this.adjustments = [
13434             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13435             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13436         ];
13437     }
13438
13439     if(this.draggable){
13440         this.dd = this.dynamic ?
13441             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13442         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13443     }
13444
13445     // public events
13446     this.addEvents({
13447         /**
13448          * @event beforeresize
13449          * Fired before resize is allowed. Set enabled to false to cancel resize.
13450          * @param {Roo.Resizable} this
13451          * @param {Roo.EventObject} e The mousedown event
13452          */
13453         "beforeresize" : true,
13454         /**
13455          * @event resize
13456          * Fired after a resize.
13457          * @param {Roo.Resizable} this
13458          * @param {Number} width The new width
13459          * @param {Number} height The new height
13460          * @param {Roo.EventObject} e The mouseup event
13461          */
13462         "resize" : true
13463     });
13464
13465     if(this.width !== null && this.height !== null){
13466         this.resizeTo(this.width, this.height);
13467     }else{
13468         this.updateChildSize();
13469     }
13470     if(Roo.isIE){
13471         this.el.dom.style.zoom = 1;
13472     }
13473     Roo.Resizable.superclass.constructor.call(this);
13474 };
13475
13476 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13477         resizeChild : false,
13478         adjustments : [0, 0],
13479         minWidth : 5,
13480         minHeight : 5,
13481         maxWidth : 10000,
13482         maxHeight : 10000,
13483         enabled : true,
13484         animate : false,
13485         duration : .35,
13486         dynamic : false,
13487         handles : false,
13488         multiDirectional : false,
13489         disableTrackOver : false,
13490         easing : 'easeOutStrong',
13491         widthIncrement : 0,
13492         heightIncrement : 0,
13493         pinned : false,
13494         width : null,
13495         height : null,
13496         preserveRatio : false,
13497         transparent: false,
13498         minX: 0,
13499         minY: 0,
13500         draggable: false,
13501
13502         /**
13503          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13504          */
13505         constrainTo: undefined,
13506         /**
13507          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13508          */
13509         resizeRegion: undefined,
13510
13511
13512     /**
13513      * Perform a manual resize
13514      * @param {Number} width
13515      * @param {Number} height
13516      */
13517     resizeTo : function(width, height){
13518         this.el.setSize(width, height);
13519         this.updateChildSize();
13520         this.fireEvent("resize", this, width, height, null);
13521     },
13522
13523     // private
13524     startSizing : function(e, handle){
13525         this.fireEvent("beforeresize", this, e);
13526         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13527
13528             if(!this.overlay){
13529                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13530                 this.overlay.unselectable();
13531                 this.overlay.enableDisplayMode("block");
13532                 this.overlay.on("mousemove", this.onMouseMove, this);
13533                 this.overlay.on("mouseup", this.onMouseUp, this);
13534             }
13535             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13536
13537             this.resizing = true;
13538             this.startBox = this.el.getBox();
13539             this.startPoint = e.getXY();
13540             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13541                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13542
13543             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13544             this.overlay.show();
13545
13546             if(this.constrainTo) {
13547                 var ct = Roo.get(this.constrainTo);
13548                 this.resizeRegion = ct.getRegion().adjust(
13549                     ct.getFrameWidth('t'),
13550                     ct.getFrameWidth('l'),
13551                     -ct.getFrameWidth('b'),
13552                     -ct.getFrameWidth('r')
13553                 );
13554             }
13555
13556             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13557             this.proxy.show();
13558             this.proxy.setBox(this.startBox);
13559             if(!this.dynamic){
13560                 this.proxy.setStyle('visibility', 'visible');
13561             }
13562         }
13563     },
13564
13565     // private
13566     onMouseDown : function(handle, e){
13567         if(this.enabled){
13568             e.stopEvent();
13569             this.activeHandle = handle;
13570             this.startSizing(e, handle);
13571         }
13572     },
13573
13574     // private
13575     onMouseUp : function(e){
13576         var size = this.resizeElement();
13577         this.resizing = false;
13578         this.handleOut();
13579         this.overlay.hide();
13580         this.proxy.hide();
13581         this.fireEvent("resize", this, size.width, size.height, e);
13582     },
13583
13584     // private
13585     updateChildSize : function(){
13586         if(this.resizeChild){
13587             var el = this.el;
13588             var child = this.resizeChild;
13589             var adj = this.adjustments;
13590             if(el.dom.offsetWidth){
13591                 var b = el.getSize(true);
13592                 child.setSize(b.width+adj[0], b.height+adj[1]);
13593             }
13594             // Second call here for IE
13595             // The first call enables instant resizing and
13596             // the second call corrects scroll bars if they
13597             // exist
13598             if(Roo.isIE){
13599                 setTimeout(function(){
13600                     if(el.dom.offsetWidth){
13601                         var b = el.getSize(true);
13602                         child.setSize(b.width+adj[0], b.height+adj[1]);
13603                     }
13604                 }, 10);
13605             }
13606         }
13607     },
13608
13609     // private
13610     snap : function(value, inc, min){
13611         if(!inc || !value) return value;
13612         var newValue = value;
13613         var m = value % inc;
13614         if(m > 0){
13615             if(m > (inc/2)){
13616                 newValue = value + (inc-m);
13617             }else{
13618                 newValue = value - m;
13619             }
13620         }
13621         return Math.max(min, newValue);
13622     },
13623
13624     // private
13625     resizeElement : function(){
13626         var box = this.proxy.getBox();
13627         if(this.updateBox){
13628             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13629         }else{
13630             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13631         }
13632         this.updateChildSize();
13633         if(!this.dynamic){
13634             this.proxy.hide();
13635         }
13636         return box;
13637     },
13638
13639     // private
13640     constrain : function(v, diff, m, mx){
13641         if(v - diff < m){
13642             diff = v - m;
13643         }else if(v - diff > mx){
13644             diff = mx - v;
13645         }
13646         return diff;
13647     },
13648
13649     // private
13650     onMouseMove : function(e){
13651         if(this.enabled){
13652             try{// try catch so if something goes wrong the user doesn't get hung
13653
13654             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13655                 return;
13656             }
13657
13658             //var curXY = this.startPoint;
13659             var curSize = this.curSize || this.startBox;
13660             var x = this.startBox.x, y = this.startBox.y;
13661             var ox = x, oy = y;
13662             var w = curSize.width, h = curSize.height;
13663             var ow = w, oh = h;
13664             var mw = this.minWidth, mh = this.minHeight;
13665             var mxw = this.maxWidth, mxh = this.maxHeight;
13666             var wi = this.widthIncrement;
13667             var hi = this.heightIncrement;
13668
13669             var eventXY = e.getXY();
13670             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13671             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13672
13673             var pos = this.activeHandle.position;
13674
13675             switch(pos){
13676                 case "east":
13677                     w += diffX;
13678                     w = Math.min(Math.max(mw, w), mxw);
13679                     break;
13680              
13681                 case "south":
13682                     h += diffY;
13683                     h = Math.min(Math.max(mh, h), mxh);
13684                     break;
13685                 case "southeast":
13686                     w += diffX;
13687                     h += diffY;
13688                     w = Math.min(Math.max(mw, w), mxw);
13689                     h = Math.min(Math.max(mh, h), mxh);
13690                     break;
13691                 case "north":
13692                     diffY = this.constrain(h, diffY, mh, mxh);
13693                     y += diffY;
13694                     h -= diffY;
13695                     break;
13696                 case "hdrag":
13697                     
13698                     if (wi) {
13699                         var adiffX = Math.abs(diffX);
13700                         var sub = (adiffX % wi); // how much 
13701                         if (sub > (wi/2)) { // far enough to snap
13702                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13703                         } else {
13704                             // remove difference.. 
13705                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13706                         }
13707                     }
13708                     x += diffX;
13709                     x = Math.max(this.minX, x);
13710                     break;
13711                 case "west":
13712                     diffX = this.constrain(w, diffX, mw, mxw);
13713                     x += diffX;
13714                     w -= diffX;
13715                     break;
13716                 case "northeast":
13717                     w += diffX;
13718                     w = Math.min(Math.max(mw, w), mxw);
13719                     diffY = this.constrain(h, diffY, mh, mxh);
13720                     y += diffY;
13721                     h -= diffY;
13722                     break;
13723                 case "northwest":
13724                     diffX = this.constrain(w, diffX, mw, mxw);
13725                     diffY = this.constrain(h, diffY, mh, mxh);
13726                     y += diffY;
13727                     h -= diffY;
13728                     x += diffX;
13729                     w -= diffX;
13730                     break;
13731                case "southwest":
13732                     diffX = this.constrain(w, diffX, mw, mxw);
13733                     h += diffY;
13734                     h = Math.min(Math.max(mh, h), mxh);
13735                     x += diffX;
13736                     w -= diffX;
13737                     break;
13738             }
13739
13740             var sw = this.snap(w, wi, mw);
13741             var sh = this.snap(h, hi, mh);
13742             if(sw != w || sh != h){
13743                 switch(pos){
13744                     case "northeast":
13745                         y -= sh - h;
13746                     break;
13747                     case "north":
13748                         y -= sh - h;
13749                         break;
13750                     case "southwest":
13751                         x -= sw - w;
13752                     break;
13753                     case "west":
13754                         x -= sw - w;
13755                         break;
13756                     case "northwest":
13757                         x -= sw - w;
13758                         y -= sh - h;
13759                     break;
13760                 }
13761                 w = sw;
13762                 h = sh;
13763             }
13764
13765             if(this.preserveRatio){
13766                 switch(pos){
13767                     case "southeast":
13768                     case "east":
13769                         h = oh * (w/ow);
13770                         h = Math.min(Math.max(mh, h), mxh);
13771                         w = ow * (h/oh);
13772                        break;
13773                     case "south":
13774                         w = ow * (h/oh);
13775                         w = Math.min(Math.max(mw, w), mxw);
13776                         h = oh * (w/ow);
13777                         break;
13778                     case "northeast":
13779                         w = ow * (h/oh);
13780                         w = Math.min(Math.max(mw, w), mxw);
13781                         h = oh * (w/ow);
13782                     break;
13783                     case "north":
13784                         var tw = w;
13785                         w = ow * (h/oh);
13786                         w = Math.min(Math.max(mw, w), mxw);
13787                         h = oh * (w/ow);
13788                         x += (tw - w) / 2;
13789                         break;
13790                     case "southwest":
13791                         h = oh * (w/ow);
13792                         h = Math.min(Math.max(mh, h), mxh);
13793                         var tw = w;
13794                         w = ow * (h/oh);
13795                         x += tw - w;
13796                         break;
13797                     case "west":
13798                         var th = h;
13799                         h = oh * (w/ow);
13800                         h = Math.min(Math.max(mh, h), mxh);
13801                         y += (th - h) / 2;
13802                         var tw = w;
13803                         w = ow * (h/oh);
13804                         x += tw - w;
13805                        break;
13806                     case "northwest":
13807                         var tw = w;
13808                         var th = h;
13809                         h = oh * (w/ow);
13810                         h = Math.min(Math.max(mh, h), mxh);
13811                         w = ow * (h/oh);
13812                         y += th - h;
13813                         x += tw - w;
13814                        break;
13815
13816                 }
13817             }
13818             if (pos == 'hdrag') {
13819                 w = ow;
13820             }
13821             this.proxy.setBounds(x, y, w, h);
13822             if(this.dynamic){
13823                 this.resizeElement();
13824             }
13825             }catch(e){}
13826         }
13827     },
13828
13829     // private
13830     handleOver : function(){
13831         if(this.enabled){
13832             this.el.addClass("x-resizable-over");
13833         }
13834     },
13835
13836     // private
13837     handleOut : function(){
13838         if(!this.resizing){
13839             this.el.removeClass("x-resizable-over");
13840         }
13841     },
13842
13843     /**
13844      * Returns the element this component is bound to.
13845      * @return {Roo.Element}
13846      */
13847     getEl : function(){
13848         return this.el;
13849     },
13850
13851     /**
13852      * Returns the resizeChild element (or null).
13853      * @return {Roo.Element}
13854      */
13855     getResizeChild : function(){
13856         return this.resizeChild;
13857     },
13858
13859     /**
13860      * Destroys this resizable. If the element was wrapped and
13861      * removeEl is not true then the element remains.
13862      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13863      */
13864     destroy : function(removeEl){
13865         this.proxy.remove();
13866         if(this.overlay){
13867             this.overlay.removeAllListeners();
13868             this.overlay.remove();
13869         }
13870         var ps = Roo.Resizable.positions;
13871         for(var k in ps){
13872             if(typeof ps[k] != "function" && this[ps[k]]){
13873                 var h = this[ps[k]];
13874                 h.el.removeAllListeners();
13875                 h.el.remove();
13876             }
13877         }
13878         if(removeEl){
13879             this.el.update("");
13880             this.el.remove();
13881         }
13882     }
13883 });
13884
13885 // private
13886 // hash to map config positions to true positions
13887 Roo.Resizable.positions = {
13888     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13889     hd: "hdrag"
13890 };
13891
13892 // private
13893 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13894     if(!this.tpl){
13895         // only initialize the template if resizable is used
13896         var tpl = Roo.DomHelper.createTemplate(
13897             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13898         );
13899         tpl.compile();
13900         Roo.Resizable.Handle.prototype.tpl = tpl;
13901     }
13902     this.position = pos;
13903     this.rz = rz;
13904     // show north drag fro topdra
13905     var handlepos = pos == 'hdrag' ? 'north' : pos;
13906     
13907     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13908     if (pos == 'hdrag') {
13909         this.el.setStyle('cursor', 'pointer');
13910     }
13911     this.el.unselectable();
13912     if(transparent){
13913         this.el.setOpacity(0);
13914     }
13915     this.el.on("mousedown", this.onMouseDown, this);
13916     if(!disableTrackOver){
13917         this.el.on("mouseover", this.onMouseOver, this);
13918         this.el.on("mouseout", this.onMouseOut, this);
13919     }
13920 };
13921
13922 // private
13923 Roo.Resizable.Handle.prototype = {
13924     afterResize : function(rz){
13925         // do nothing
13926     },
13927     // private
13928     onMouseDown : function(e){
13929         this.rz.onMouseDown(this, e);
13930     },
13931     // private
13932     onMouseOver : function(e){
13933         this.rz.handleOver(this, e);
13934     },
13935     // private
13936     onMouseOut : function(e){
13937         this.rz.handleOut(this, e);
13938     }
13939 };/*
13940  * Based on:
13941  * Ext JS Library 1.1.1
13942  * Copyright(c) 2006-2007, Ext JS, LLC.
13943  *
13944  * Originally Released Under LGPL - original licence link has changed is not relivant.
13945  *
13946  * Fork - LGPL
13947  * <script type="text/javascript">
13948  */
13949
13950 /**
13951  * @class Roo.Editor
13952  * @extends Roo.Component
13953  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13954  * @constructor
13955  * Create a new Editor
13956  * @param {Roo.form.Field} field The Field object (or descendant)
13957  * @param {Object} config The config object
13958  */
13959 Roo.Editor = function(field, config){
13960     Roo.Editor.superclass.constructor.call(this, config);
13961     this.field = field;
13962     this.addEvents({
13963         /**
13964              * @event beforestartedit
13965              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13966              * false from the handler of this event.
13967              * @param {Editor} this
13968              * @param {Roo.Element} boundEl The underlying element bound to this editor
13969              * @param {Mixed} value The field value being set
13970              */
13971         "beforestartedit" : true,
13972         /**
13973              * @event startedit
13974              * Fires when this editor is displayed
13975              * @param {Roo.Element} boundEl The underlying element bound to this editor
13976              * @param {Mixed} value The starting field value
13977              */
13978         "startedit" : true,
13979         /**
13980              * @event beforecomplete
13981              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13982              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13983              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13984              * event will not fire since no edit actually occurred.
13985              * @param {Editor} this
13986              * @param {Mixed} value The current field value
13987              * @param {Mixed} startValue The original field value
13988              */
13989         "beforecomplete" : true,
13990         /**
13991              * @event complete
13992              * Fires after editing is complete and any changed value has been written to the underlying field.
13993              * @param {Editor} this
13994              * @param {Mixed} value The current field value
13995              * @param {Mixed} startValue The original field value
13996              */
13997         "complete" : true,
13998         /**
13999          * @event specialkey
14000          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
14001          * {@link Roo.EventObject#getKey} to determine which key was pressed.
14002          * @param {Roo.form.Field} this
14003          * @param {Roo.EventObject} e The event object
14004          */
14005         "specialkey" : true
14006     });
14007 };
14008
14009 Roo.extend(Roo.Editor, Roo.Component, {
14010     /**
14011      * @cfg {Boolean/String} autosize
14012      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14013      * or "height" to adopt the height only (defaults to false)
14014      */
14015     /**
14016      * @cfg {Boolean} revertInvalid
14017      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14018      * validation fails (defaults to true)
14019      */
14020     /**
14021      * @cfg {Boolean} ignoreNoChange
14022      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14023      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14024      * will never be ignored.
14025      */
14026     /**
14027      * @cfg {Boolean} hideEl
14028      * False to keep the bound element visible while the editor is displayed (defaults to true)
14029      */
14030     /**
14031      * @cfg {Mixed} value
14032      * The data value of the underlying field (defaults to "")
14033      */
14034     value : "",
14035     /**
14036      * @cfg {String} alignment
14037      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14038      */
14039     alignment: "c-c?",
14040     /**
14041      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14042      * for bottom-right shadow (defaults to "frame")
14043      */
14044     shadow : "frame",
14045     /**
14046      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14047      */
14048     constrain : false,
14049     /**
14050      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14051      */
14052     completeOnEnter : false,
14053     /**
14054      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14055      */
14056     cancelOnEsc : false,
14057     /**
14058      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14059      */
14060     updateEl : false,
14061
14062     // private
14063     onRender : function(ct, position){
14064         this.el = new Roo.Layer({
14065             shadow: this.shadow,
14066             cls: "x-editor",
14067             parentEl : ct,
14068             shim : this.shim,
14069             shadowOffset:4,
14070             id: this.id,
14071             constrain: this.constrain
14072         });
14073         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14074         if(this.field.msgTarget != 'title'){
14075             this.field.msgTarget = 'qtip';
14076         }
14077         this.field.render(this.el);
14078         if(Roo.isGecko){
14079             this.field.el.dom.setAttribute('autocomplete', 'off');
14080         }
14081         this.field.on("specialkey", this.onSpecialKey, this);
14082         if(this.swallowKeys){
14083             this.field.el.swallowEvent(['keydown','keypress']);
14084         }
14085         this.field.show();
14086         this.field.on("blur", this.onBlur, this);
14087         if(this.field.grow){
14088             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14089         }
14090     },
14091
14092     onSpecialKey : function(field, e)
14093     {
14094         //Roo.log('editor onSpecialKey');
14095         if(this.completeOnEnter && e.getKey() == e.ENTER){
14096             e.stopEvent();
14097             this.completeEdit();
14098             return;
14099         }
14100         // do not fire special key otherwise it might hide close the editor...
14101         if(e.getKey() == e.ENTER){    
14102             return;
14103         }
14104         if(this.cancelOnEsc && e.getKey() == e.ESC){
14105             this.cancelEdit();
14106             return;
14107         } 
14108         this.fireEvent('specialkey', field, e);
14109     
14110     },
14111
14112     /**
14113      * Starts the editing process and shows the editor.
14114      * @param {String/HTMLElement/Element} el The element to edit
14115      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14116       * to the innerHTML of el.
14117      */
14118     startEdit : function(el, value){
14119         if(this.editing){
14120             this.completeEdit();
14121         }
14122         this.boundEl = Roo.get(el);
14123         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14124         if(!this.rendered){
14125             this.render(this.parentEl || document.body);
14126         }
14127         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14128             return;
14129         }
14130         this.startValue = v;
14131         this.field.setValue(v);
14132         if(this.autoSize){
14133             var sz = this.boundEl.getSize();
14134             switch(this.autoSize){
14135                 case "width":
14136                 this.setSize(sz.width,  "");
14137                 break;
14138                 case "height":
14139                 this.setSize("",  sz.height);
14140                 break;
14141                 default:
14142                 this.setSize(sz.width,  sz.height);
14143             }
14144         }
14145         this.el.alignTo(this.boundEl, this.alignment);
14146         this.editing = true;
14147         if(Roo.QuickTips){
14148             Roo.QuickTips.disable();
14149         }
14150         this.show();
14151     },
14152
14153     /**
14154      * Sets the height and width of this editor.
14155      * @param {Number} width The new width
14156      * @param {Number} height The new height
14157      */
14158     setSize : function(w, h){
14159         this.field.setSize(w, h);
14160         if(this.el){
14161             this.el.sync();
14162         }
14163     },
14164
14165     /**
14166      * Realigns the editor to the bound field based on the current alignment config value.
14167      */
14168     realign : function(){
14169         this.el.alignTo(this.boundEl, this.alignment);
14170     },
14171
14172     /**
14173      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14174      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14175      */
14176     completeEdit : function(remainVisible){
14177         if(!this.editing){
14178             return;
14179         }
14180         var v = this.getValue();
14181         if(this.revertInvalid !== false && !this.field.isValid()){
14182             v = this.startValue;
14183             this.cancelEdit(true);
14184         }
14185         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14186             this.editing = false;
14187             this.hide();
14188             return;
14189         }
14190         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14191             this.editing = false;
14192             if(this.updateEl && this.boundEl){
14193                 this.boundEl.update(v);
14194             }
14195             if(remainVisible !== true){
14196                 this.hide();
14197             }
14198             this.fireEvent("complete", this, v, this.startValue);
14199         }
14200     },
14201
14202     // private
14203     onShow : function(){
14204         this.el.show();
14205         if(this.hideEl !== false){
14206             this.boundEl.hide();
14207         }
14208         this.field.show();
14209         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14210             this.fixIEFocus = true;
14211             this.deferredFocus.defer(50, this);
14212         }else{
14213             this.field.focus();
14214         }
14215         this.fireEvent("startedit", this.boundEl, this.startValue);
14216     },
14217
14218     deferredFocus : function(){
14219         if(this.editing){
14220             this.field.focus();
14221         }
14222     },
14223
14224     /**
14225      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14226      * reverted to the original starting value.
14227      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14228      * cancel (defaults to false)
14229      */
14230     cancelEdit : function(remainVisible){
14231         if(this.editing){
14232             this.setValue(this.startValue);
14233             if(remainVisible !== true){
14234                 this.hide();
14235             }
14236         }
14237     },
14238
14239     // private
14240     onBlur : function(){
14241         if(this.allowBlur !== true && this.editing){
14242             this.completeEdit();
14243         }
14244     },
14245
14246     // private
14247     onHide : function(){
14248         if(this.editing){
14249             this.completeEdit();
14250             return;
14251         }
14252         this.field.blur();
14253         if(this.field.collapse){
14254             this.field.collapse();
14255         }
14256         this.el.hide();
14257         if(this.hideEl !== false){
14258             this.boundEl.show();
14259         }
14260         if(Roo.QuickTips){
14261             Roo.QuickTips.enable();
14262         }
14263     },
14264
14265     /**
14266      * Sets the data value of the editor
14267      * @param {Mixed} value Any valid value supported by the underlying field
14268      */
14269     setValue : function(v){
14270         this.field.setValue(v);
14271     },
14272
14273     /**
14274      * Gets the data value of the editor
14275      * @return {Mixed} The data value
14276      */
14277     getValue : function(){
14278         return this.field.getValue();
14279     }
14280 });/*
14281  * Based on:
14282  * Ext JS Library 1.1.1
14283  * Copyright(c) 2006-2007, Ext JS, LLC.
14284  *
14285  * Originally Released Under LGPL - original licence link has changed is not relivant.
14286  *
14287  * Fork - LGPL
14288  * <script type="text/javascript">
14289  */
14290  
14291 /**
14292  * @class Roo.BasicDialog
14293  * @extends Roo.util.Observable
14294  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14295  * <pre><code>
14296 var dlg = new Roo.BasicDialog("my-dlg", {
14297     height: 200,
14298     width: 300,
14299     minHeight: 100,
14300     minWidth: 150,
14301     modal: true,
14302     proxyDrag: true,
14303     shadow: true
14304 });
14305 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14306 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14307 dlg.addButton('Cancel', dlg.hide, dlg);
14308 dlg.show();
14309 </code></pre>
14310   <b>A Dialog should always be a direct child of the body element.</b>
14311  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14312  * @cfg {String} title Default text to display in the title bar (defaults to null)
14313  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14314  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14315  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14316  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14317  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14318  * (defaults to null with no animation)
14319  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14320  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14321  * property for valid values (defaults to 'all')
14322  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14323  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14324  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14325  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14326  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14327  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14328  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14329  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14330  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14331  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14332  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14333  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14334  * draggable = true (defaults to false)
14335  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14336  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14337  * shadow (defaults to false)
14338  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14339  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14340  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14341  * @cfg {Array} buttons Array of buttons
14342  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14343  * @constructor
14344  * Create a new BasicDialog.
14345  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14346  * @param {Object} config Configuration options
14347  */
14348 Roo.BasicDialog = function(el, config){
14349     this.el = Roo.get(el);
14350     var dh = Roo.DomHelper;
14351     if(!this.el && config && config.autoCreate){
14352         if(typeof config.autoCreate == "object"){
14353             if(!config.autoCreate.id){
14354                 config.autoCreate.id = el;
14355             }
14356             this.el = dh.append(document.body,
14357                         config.autoCreate, true);
14358         }else{
14359             this.el = dh.append(document.body,
14360                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14361         }
14362     }
14363     el = this.el;
14364     el.setDisplayed(true);
14365     el.hide = this.hideAction;
14366     this.id = el.id;
14367     el.addClass("x-dlg");
14368
14369     Roo.apply(this, config);
14370
14371     this.proxy = el.createProxy("x-dlg-proxy");
14372     this.proxy.hide = this.hideAction;
14373     this.proxy.setOpacity(.5);
14374     this.proxy.hide();
14375
14376     if(config.width){
14377         el.setWidth(config.width);
14378     }
14379     if(config.height){
14380         el.setHeight(config.height);
14381     }
14382     this.size = el.getSize();
14383     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14384         this.xy = [config.x,config.y];
14385     }else{
14386         this.xy = el.getCenterXY(true);
14387     }
14388     /** The header element @type Roo.Element */
14389     this.header = el.child("> .x-dlg-hd");
14390     /** The body element @type Roo.Element */
14391     this.body = el.child("> .x-dlg-bd");
14392     /** The footer element @type Roo.Element */
14393     this.footer = el.child("> .x-dlg-ft");
14394
14395     if(!this.header){
14396         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14397     }
14398     if(!this.body){
14399         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14400     }
14401
14402     this.header.unselectable();
14403     if(this.title){
14404         this.header.update(this.title);
14405     }
14406     // this element allows the dialog to be focused for keyboard event
14407     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14408     this.focusEl.swallowEvent("click", true);
14409
14410     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14411
14412     // wrap the body and footer for special rendering
14413     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14414     if(this.footer){
14415         this.bwrap.dom.appendChild(this.footer.dom);
14416     }
14417
14418     this.bg = this.el.createChild({
14419         tag: "div", cls:"x-dlg-bg",
14420         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14421     });
14422     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14423
14424
14425     if(this.autoScroll !== false && !this.autoTabs){
14426         this.body.setStyle("overflow", "auto");
14427     }
14428
14429     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14430
14431     if(this.closable !== false){
14432         this.el.addClass("x-dlg-closable");
14433         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14434         this.close.on("click", this.closeClick, this);
14435         this.close.addClassOnOver("x-dlg-close-over");
14436     }
14437     if(this.collapsible !== false){
14438         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14439         this.collapseBtn.on("click", this.collapseClick, this);
14440         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14441         this.header.on("dblclick", this.collapseClick, this);
14442     }
14443     if(this.resizable !== false){
14444         this.el.addClass("x-dlg-resizable");
14445         this.resizer = new Roo.Resizable(el, {
14446             minWidth: this.minWidth || 80,
14447             minHeight:this.minHeight || 80,
14448             handles: this.resizeHandles || "all",
14449             pinned: true
14450         });
14451         this.resizer.on("beforeresize", this.beforeResize, this);
14452         this.resizer.on("resize", this.onResize, this);
14453     }
14454     if(this.draggable !== false){
14455         el.addClass("x-dlg-draggable");
14456         if (!this.proxyDrag) {
14457             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14458         }
14459         else {
14460             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14461         }
14462         dd.setHandleElId(this.header.id);
14463         dd.endDrag = this.endMove.createDelegate(this);
14464         dd.startDrag = this.startMove.createDelegate(this);
14465         dd.onDrag = this.onDrag.createDelegate(this);
14466         dd.scroll = false;
14467         this.dd = dd;
14468     }
14469     if(this.modal){
14470         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14471         this.mask.enableDisplayMode("block");
14472         this.mask.hide();
14473         this.el.addClass("x-dlg-modal");
14474     }
14475     if(this.shadow){
14476         this.shadow = new Roo.Shadow({
14477             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14478             offset : this.shadowOffset
14479         });
14480     }else{
14481         this.shadowOffset = 0;
14482     }
14483     if(Roo.useShims && this.shim !== false){
14484         this.shim = this.el.createShim();
14485         this.shim.hide = this.hideAction;
14486         this.shim.hide();
14487     }else{
14488         this.shim = false;
14489     }
14490     if(this.autoTabs){
14491         this.initTabs();
14492     }
14493     if (this.buttons) { 
14494         var bts= this.buttons;
14495         this.buttons = [];
14496         Roo.each(bts, function(b) {
14497             this.addButton(b);
14498         }, this);
14499     }
14500     
14501     
14502     this.addEvents({
14503         /**
14504          * @event keydown
14505          * Fires when a key is pressed
14506          * @param {Roo.BasicDialog} this
14507          * @param {Roo.EventObject} e
14508          */
14509         "keydown" : true,
14510         /**
14511          * @event move
14512          * Fires when this dialog is moved by the user.
14513          * @param {Roo.BasicDialog} this
14514          * @param {Number} x The new page X
14515          * @param {Number} y The new page Y
14516          */
14517         "move" : true,
14518         /**
14519          * @event resize
14520          * Fires when this dialog is resized by the user.
14521          * @param {Roo.BasicDialog} this
14522          * @param {Number} width The new width
14523          * @param {Number} height The new height
14524          */
14525         "resize" : true,
14526         /**
14527          * @event beforehide
14528          * Fires before this dialog is hidden.
14529          * @param {Roo.BasicDialog} this
14530          */
14531         "beforehide" : true,
14532         /**
14533          * @event hide
14534          * Fires when this dialog is hidden.
14535          * @param {Roo.BasicDialog} this
14536          */
14537         "hide" : true,
14538         /**
14539          * @event beforeshow
14540          * Fires before this dialog is shown.
14541          * @param {Roo.BasicDialog} this
14542          */
14543         "beforeshow" : true,
14544         /**
14545          * @event show
14546          * Fires when this dialog is shown.
14547          * @param {Roo.BasicDialog} this
14548          */
14549         "show" : true
14550     });
14551     el.on("keydown", this.onKeyDown, this);
14552     el.on("mousedown", this.toFront, this);
14553     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14554     this.el.hide();
14555     Roo.DialogManager.register(this);
14556     Roo.BasicDialog.superclass.constructor.call(this);
14557 };
14558
14559 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14560     shadowOffset: Roo.isIE ? 6 : 5,
14561     minHeight: 80,
14562     minWidth: 200,
14563     minButtonWidth: 75,
14564     defaultButton: null,
14565     buttonAlign: "right",
14566     tabTag: 'div',
14567     firstShow: true,
14568
14569     /**
14570      * Sets the dialog title text
14571      * @param {String} text The title text to display
14572      * @return {Roo.BasicDialog} this
14573      */
14574     setTitle : function(text){
14575         this.header.update(text);
14576         return this;
14577     },
14578
14579     // private
14580     closeClick : function(){
14581         this.hide();
14582     },
14583
14584     // private
14585     collapseClick : function(){
14586         this[this.collapsed ? "expand" : "collapse"]();
14587     },
14588
14589     /**
14590      * Collapses the dialog to its minimized state (only the title bar is visible).
14591      * Equivalent to the user clicking the collapse dialog button.
14592      */
14593     collapse : function(){
14594         if(!this.collapsed){
14595             this.collapsed = true;
14596             this.el.addClass("x-dlg-collapsed");
14597             this.restoreHeight = this.el.getHeight();
14598             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14599         }
14600     },
14601
14602     /**
14603      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14604      * clicking the expand dialog button.
14605      */
14606     expand : function(){
14607         if(this.collapsed){
14608             this.collapsed = false;
14609             this.el.removeClass("x-dlg-collapsed");
14610             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14611         }
14612     },
14613
14614     /**
14615      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14616      * @return {Roo.TabPanel} The tabs component
14617      */
14618     initTabs : function(){
14619         var tabs = this.getTabs();
14620         while(tabs.getTab(0)){
14621             tabs.removeTab(0);
14622         }
14623         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14624             var dom = el.dom;
14625             tabs.addTab(Roo.id(dom), dom.title);
14626             dom.title = "";
14627         });
14628         tabs.activate(0);
14629         return tabs;
14630     },
14631
14632     // private
14633     beforeResize : function(){
14634         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14635     },
14636
14637     // private
14638     onResize : function(){
14639         this.refreshSize();
14640         this.syncBodyHeight();
14641         this.adjustAssets();
14642         this.focus();
14643         this.fireEvent("resize", this, this.size.width, this.size.height);
14644     },
14645
14646     // private
14647     onKeyDown : function(e){
14648         if(this.isVisible()){
14649             this.fireEvent("keydown", this, e);
14650         }
14651     },
14652
14653     /**
14654      * Resizes the dialog.
14655      * @param {Number} width
14656      * @param {Number} height
14657      * @return {Roo.BasicDialog} this
14658      */
14659     resizeTo : function(width, height){
14660         this.el.setSize(width, height);
14661         this.size = {width: width, height: height};
14662         this.syncBodyHeight();
14663         if(this.fixedcenter){
14664             this.center();
14665         }
14666         if(this.isVisible()){
14667             this.constrainXY();
14668             this.adjustAssets();
14669         }
14670         this.fireEvent("resize", this, width, height);
14671         return this;
14672     },
14673
14674
14675     /**
14676      * Resizes the dialog to fit the specified content size.
14677      * @param {Number} width
14678      * @param {Number} height
14679      * @return {Roo.BasicDialog} this
14680      */
14681     setContentSize : function(w, h){
14682         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14683         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14684         //if(!this.el.isBorderBox()){
14685             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14686             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14687         //}
14688         if(this.tabs){
14689             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14690             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14691         }
14692         this.resizeTo(w, h);
14693         return this;
14694     },
14695
14696     /**
14697      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14698      * executed in response to a particular key being pressed while the dialog is active.
14699      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14700      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14701      * @param {Function} fn The function to call
14702      * @param {Object} scope (optional) The scope of the function
14703      * @return {Roo.BasicDialog} this
14704      */
14705     addKeyListener : function(key, fn, scope){
14706         var keyCode, shift, ctrl, alt;
14707         if(typeof key == "object" && !(key instanceof Array)){
14708             keyCode = key["key"];
14709             shift = key["shift"];
14710             ctrl = key["ctrl"];
14711             alt = key["alt"];
14712         }else{
14713             keyCode = key;
14714         }
14715         var handler = function(dlg, e){
14716             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14717                 var k = e.getKey();
14718                 if(keyCode instanceof Array){
14719                     for(var i = 0, len = keyCode.length; i < len; i++){
14720                         if(keyCode[i] == k){
14721                           fn.call(scope || window, dlg, k, e);
14722                           return;
14723                         }
14724                     }
14725                 }else{
14726                     if(k == keyCode){
14727                         fn.call(scope || window, dlg, k, e);
14728                     }
14729                 }
14730             }
14731         };
14732         this.on("keydown", handler);
14733         return this;
14734     },
14735
14736     /**
14737      * Returns the TabPanel component (creates it if it doesn't exist).
14738      * Note: If you wish to simply check for the existence of tabs without creating them,
14739      * check for a null 'tabs' property.
14740      * @return {Roo.TabPanel} The tabs component
14741      */
14742     getTabs : function(){
14743         if(!this.tabs){
14744             this.el.addClass("x-dlg-auto-tabs");
14745             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14746             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14747         }
14748         return this.tabs;
14749     },
14750
14751     /**
14752      * Adds a button to the footer section of the dialog.
14753      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14754      * object or a valid Roo.DomHelper element config
14755      * @param {Function} handler The function called when the button is clicked
14756      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14757      * @return {Roo.Button} The new button
14758      */
14759     addButton : function(config, handler, scope){
14760         var dh = Roo.DomHelper;
14761         if(!this.footer){
14762             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14763         }
14764         if(!this.btnContainer){
14765             var tb = this.footer.createChild({
14766
14767                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14768                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14769             }, null, true);
14770             this.btnContainer = tb.firstChild.firstChild.firstChild;
14771         }
14772         var bconfig = {
14773             handler: handler,
14774             scope: scope,
14775             minWidth: this.minButtonWidth,
14776             hideParent:true
14777         };
14778         if(typeof config == "string"){
14779             bconfig.text = config;
14780         }else{
14781             if(config.tag){
14782                 bconfig.dhconfig = config;
14783             }else{
14784                 Roo.apply(bconfig, config);
14785             }
14786         }
14787         var fc = false;
14788         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14789             bconfig.position = Math.max(0, bconfig.position);
14790             fc = this.btnContainer.childNodes[bconfig.position];
14791         }
14792          
14793         var btn = new Roo.Button(
14794             fc ? 
14795                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14796                 : this.btnContainer.appendChild(document.createElement("td")),
14797             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14798             bconfig
14799         );
14800         this.syncBodyHeight();
14801         if(!this.buttons){
14802             /**
14803              * Array of all the buttons that have been added to this dialog via addButton
14804              * @type Array
14805              */
14806             this.buttons = [];
14807         }
14808         this.buttons.push(btn);
14809         return btn;
14810     },
14811
14812     /**
14813      * Sets the default button to be focused when the dialog is displayed.
14814      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14815      * @return {Roo.BasicDialog} this
14816      */
14817     setDefaultButton : function(btn){
14818         this.defaultButton = btn;
14819         return this;
14820     },
14821
14822     // private
14823     getHeaderFooterHeight : function(safe){
14824         var height = 0;
14825         if(this.header){
14826            height += this.header.getHeight();
14827         }
14828         if(this.footer){
14829            var fm = this.footer.getMargins();
14830             height += (this.footer.getHeight()+fm.top+fm.bottom);
14831         }
14832         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14833         height += this.centerBg.getPadding("tb");
14834         return height;
14835     },
14836
14837     // private
14838     syncBodyHeight : function(){
14839         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14840         var height = this.size.height - this.getHeaderFooterHeight(false);
14841         bd.setHeight(height-bd.getMargins("tb"));
14842         var hh = this.header.getHeight();
14843         var h = this.size.height-hh;
14844         cb.setHeight(h);
14845         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14846         bw.setHeight(h-cb.getPadding("tb"));
14847         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14848         bd.setWidth(bw.getWidth(true));
14849         if(this.tabs){
14850             this.tabs.syncHeight();
14851             if(Roo.isIE){
14852                 this.tabs.el.repaint();
14853             }
14854         }
14855     },
14856
14857     /**
14858      * Restores the previous state of the dialog if Roo.state is configured.
14859      * @return {Roo.BasicDialog} this
14860      */
14861     restoreState : function(){
14862         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14863         if(box && box.width){
14864             this.xy = [box.x, box.y];
14865             this.resizeTo(box.width, box.height);
14866         }
14867         return this;
14868     },
14869
14870     // private
14871     beforeShow : function(){
14872         this.expand();
14873         if(this.fixedcenter){
14874             this.xy = this.el.getCenterXY(true);
14875         }
14876         if(this.modal){
14877             Roo.get(document.body).addClass("x-body-masked");
14878             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14879             this.mask.show();
14880         }
14881         this.constrainXY();
14882     },
14883
14884     // private
14885     animShow : function(){
14886         var b = Roo.get(this.animateTarget).getBox();
14887         this.proxy.setSize(b.width, b.height);
14888         this.proxy.setLocation(b.x, b.y);
14889         this.proxy.show();
14890         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14891                     true, .35, this.showEl.createDelegate(this));
14892     },
14893
14894     /**
14895      * Shows the dialog.
14896      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14897      * @return {Roo.BasicDialog} this
14898      */
14899     show : function(animateTarget){
14900         if (this.fireEvent("beforeshow", this) === false){
14901             return;
14902         }
14903         if(this.syncHeightBeforeShow){
14904             this.syncBodyHeight();
14905         }else if(this.firstShow){
14906             this.firstShow = false;
14907             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14908         }
14909         this.animateTarget = animateTarget || this.animateTarget;
14910         if(!this.el.isVisible()){
14911             this.beforeShow();
14912             if(this.animateTarget && Roo.get(this.animateTarget)){
14913                 this.animShow();
14914             }else{
14915                 this.showEl();
14916             }
14917         }
14918         return this;
14919     },
14920
14921     // private
14922     showEl : function(){
14923         this.proxy.hide();
14924         this.el.setXY(this.xy);
14925         this.el.show();
14926         this.adjustAssets(true);
14927         this.toFront();
14928         this.focus();
14929         // IE peekaboo bug - fix found by Dave Fenwick
14930         if(Roo.isIE){
14931             this.el.repaint();
14932         }
14933         this.fireEvent("show", this);
14934     },
14935
14936     /**
14937      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14938      * dialog itself will receive focus.
14939      */
14940     focus : function(){
14941         if(this.defaultButton){
14942             this.defaultButton.focus();
14943         }else{
14944             this.focusEl.focus();
14945         }
14946     },
14947
14948     // private
14949     constrainXY : function(){
14950         if(this.constraintoviewport !== false){
14951             if(!this.viewSize){
14952                 if(this.container){
14953                     var s = this.container.getSize();
14954                     this.viewSize = [s.width, s.height];
14955                 }else{
14956                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14957                 }
14958             }
14959             var s = Roo.get(this.container||document).getScroll();
14960
14961             var x = this.xy[0], y = this.xy[1];
14962             var w = this.size.width, h = this.size.height;
14963             var vw = this.viewSize[0], vh = this.viewSize[1];
14964             // only move it if it needs it
14965             var moved = false;
14966             // first validate right/bottom
14967             if(x + w > vw+s.left){
14968                 x = vw - w;
14969                 moved = true;
14970             }
14971             if(y + h > vh+s.top){
14972                 y = vh - h;
14973                 moved = true;
14974             }
14975             // then make sure top/left isn't negative
14976             if(x < s.left){
14977                 x = s.left;
14978                 moved = true;
14979             }
14980             if(y < s.top){
14981                 y = s.top;
14982                 moved = true;
14983             }
14984             if(moved){
14985                 // cache xy
14986                 this.xy = [x, y];
14987                 if(this.isVisible()){
14988                     this.el.setLocation(x, y);
14989                     this.adjustAssets();
14990                 }
14991             }
14992         }
14993     },
14994
14995     // private
14996     onDrag : function(){
14997         if(!this.proxyDrag){
14998             this.xy = this.el.getXY();
14999             this.adjustAssets();
15000         }
15001     },
15002
15003     // private
15004     adjustAssets : function(doShow){
15005         var x = this.xy[0], y = this.xy[1];
15006         var w = this.size.width, h = this.size.height;
15007         if(doShow === true){
15008             if(this.shadow){
15009                 this.shadow.show(this.el);
15010             }
15011             if(this.shim){
15012                 this.shim.show();
15013             }
15014         }
15015         if(this.shadow && this.shadow.isVisible()){
15016             this.shadow.show(this.el);
15017         }
15018         if(this.shim && this.shim.isVisible()){
15019             this.shim.setBounds(x, y, w, h);
15020         }
15021     },
15022
15023     // private
15024     adjustViewport : function(w, h){
15025         if(!w || !h){
15026             w = Roo.lib.Dom.getViewWidth();
15027             h = Roo.lib.Dom.getViewHeight();
15028         }
15029         // cache the size
15030         this.viewSize = [w, h];
15031         if(this.modal && this.mask.isVisible()){
15032             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15033             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15034         }
15035         if(this.isVisible()){
15036             this.constrainXY();
15037         }
15038     },
15039
15040     /**
15041      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15042      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15043      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15044      */
15045     destroy : function(removeEl){
15046         if(this.isVisible()){
15047             this.animateTarget = null;
15048             this.hide();
15049         }
15050         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15051         if(this.tabs){
15052             this.tabs.destroy(removeEl);
15053         }
15054         Roo.destroy(
15055              this.shim,
15056              this.proxy,
15057              this.resizer,
15058              this.close,
15059              this.mask
15060         );
15061         if(this.dd){
15062             this.dd.unreg();
15063         }
15064         if(this.buttons){
15065            for(var i = 0, len = this.buttons.length; i < len; i++){
15066                this.buttons[i].destroy();
15067            }
15068         }
15069         this.el.removeAllListeners();
15070         if(removeEl === true){
15071             this.el.update("");
15072             this.el.remove();
15073         }
15074         Roo.DialogManager.unregister(this);
15075     },
15076
15077     // private
15078     startMove : function(){
15079         if(this.proxyDrag){
15080             this.proxy.show();
15081         }
15082         if(this.constraintoviewport !== false){
15083             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15084         }
15085     },
15086
15087     // private
15088     endMove : function(){
15089         if(!this.proxyDrag){
15090             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15091         }else{
15092             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15093             this.proxy.hide();
15094         }
15095         this.refreshSize();
15096         this.adjustAssets();
15097         this.focus();
15098         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15099     },
15100
15101     /**
15102      * Brings this dialog to the front of any other visible dialogs
15103      * @return {Roo.BasicDialog} this
15104      */
15105     toFront : function(){
15106         Roo.DialogManager.bringToFront(this);
15107         return this;
15108     },
15109
15110     /**
15111      * Sends this dialog to the back (under) of any other visible dialogs
15112      * @return {Roo.BasicDialog} this
15113      */
15114     toBack : function(){
15115         Roo.DialogManager.sendToBack(this);
15116         return this;
15117     },
15118
15119     /**
15120      * Centers this dialog in the viewport
15121      * @return {Roo.BasicDialog} this
15122      */
15123     center : function(){
15124         var xy = this.el.getCenterXY(true);
15125         this.moveTo(xy[0], xy[1]);
15126         return this;
15127     },
15128
15129     /**
15130      * Moves the dialog's top-left corner to the specified point
15131      * @param {Number} x
15132      * @param {Number} y
15133      * @return {Roo.BasicDialog} this
15134      */
15135     moveTo : function(x, y){
15136         this.xy = [x,y];
15137         if(this.isVisible()){
15138             this.el.setXY(this.xy);
15139             this.adjustAssets();
15140         }
15141         return this;
15142     },
15143
15144     /**
15145      * Aligns the dialog to the specified element
15146      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15147      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15148      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15149      * @return {Roo.BasicDialog} this
15150      */
15151     alignTo : function(element, position, offsets){
15152         this.xy = this.el.getAlignToXY(element, position, offsets);
15153         if(this.isVisible()){
15154             this.el.setXY(this.xy);
15155             this.adjustAssets();
15156         }
15157         return this;
15158     },
15159
15160     /**
15161      * Anchors an element to another element and realigns it when the window is resized.
15162      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15163      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15164      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15165      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15166      * is a number, it is used as the buffer delay (defaults to 50ms).
15167      * @return {Roo.BasicDialog} this
15168      */
15169     anchorTo : function(el, alignment, offsets, monitorScroll){
15170         var action = function(){
15171             this.alignTo(el, alignment, offsets);
15172         };
15173         Roo.EventManager.onWindowResize(action, this);
15174         var tm = typeof monitorScroll;
15175         if(tm != 'undefined'){
15176             Roo.EventManager.on(window, 'scroll', action, this,
15177                 {buffer: tm == 'number' ? monitorScroll : 50});
15178         }
15179         action.call(this);
15180         return this;
15181     },
15182
15183     /**
15184      * Returns true if the dialog is visible
15185      * @return {Boolean}
15186      */
15187     isVisible : function(){
15188         return this.el.isVisible();
15189     },
15190
15191     // private
15192     animHide : function(callback){
15193         var b = Roo.get(this.animateTarget).getBox();
15194         this.proxy.show();
15195         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15196         this.el.hide();
15197         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15198                     this.hideEl.createDelegate(this, [callback]));
15199     },
15200
15201     /**
15202      * Hides the dialog.
15203      * @param {Function} callback (optional) Function to call when the dialog is hidden
15204      * @return {Roo.BasicDialog} this
15205      */
15206     hide : function(callback){
15207         if (this.fireEvent("beforehide", this) === false){
15208             return;
15209         }
15210         if(this.shadow){
15211             this.shadow.hide();
15212         }
15213         if(this.shim) {
15214           this.shim.hide();
15215         }
15216         // sometimes animateTarget seems to get set.. causing problems...
15217         // this just double checks..
15218         if(this.animateTarget && Roo.get(this.animateTarget)) {
15219            this.animHide(callback);
15220         }else{
15221             this.el.hide();
15222             this.hideEl(callback);
15223         }
15224         return this;
15225     },
15226
15227     // private
15228     hideEl : function(callback){
15229         this.proxy.hide();
15230         if(this.modal){
15231             this.mask.hide();
15232             Roo.get(document.body).removeClass("x-body-masked");
15233         }
15234         this.fireEvent("hide", this);
15235         if(typeof callback == "function"){
15236             callback();
15237         }
15238     },
15239
15240     // private
15241     hideAction : function(){
15242         this.setLeft("-10000px");
15243         this.setTop("-10000px");
15244         this.setStyle("visibility", "hidden");
15245     },
15246
15247     // private
15248     refreshSize : function(){
15249         this.size = this.el.getSize();
15250         this.xy = this.el.getXY();
15251         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15252     },
15253
15254     // private
15255     // z-index is managed by the DialogManager and may be overwritten at any time
15256     setZIndex : function(index){
15257         if(this.modal){
15258             this.mask.setStyle("z-index", index);
15259         }
15260         if(this.shim){
15261             this.shim.setStyle("z-index", ++index);
15262         }
15263         if(this.shadow){
15264             this.shadow.setZIndex(++index);
15265         }
15266         this.el.setStyle("z-index", ++index);
15267         if(this.proxy){
15268             this.proxy.setStyle("z-index", ++index);
15269         }
15270         if(this.resizer){
15271             this.resizer.proxy.setStyle("z-index", ++index);
15272         }
15273
15274         this.lastZIndex = index;
15275     },
15276
15277     /**
15278      * Returns the element for this dialog
15279      * @return {Roo.Element} The underlying dialog Element
15280      */
15281     getEl : function(){
15282         return this.el;
15283     }
15284 });
15285
15286 /**
15287  * @class Roo.DialogManager
15288  * Provides global access to BasicDialogs that have been created and
15289  * support for z-indexing (layering) multiple open dialogs.
15290  */
15291 Roo.DialogManager = function(){
15292     var list = {};
15293     var accessList = [];
15294     var front = null;
15295
15296     // private
15297     var sortDialogs = function(d1, d2){
15298         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15299     };
15300
15301     // private
15302     var orderDialogs = function(){
15303         accessList.sort(sortDialogs);
15304         var seed = Roo.DialogManager.zseed;
15305         for(var i = 0, len = accessList.length; i < len; i++){
15306             var dlg = accessList[i];
15307             if(dlg){
15308                 dlg.setZIndex(seed + (i*10));
15309             }
15310         }
15311     };
15312
15313     return {
15314         /**
15315          * The starting z-index for BasicDialogs (defaults to 9000)
15316          * @type Number The z-index value
15317          */
15318         zseed : 9000,
15319
15320         // private
15321         register : function(dlg){
15322             list[dlg.id] = dlg;
15323             accessList.push(dlg);
15324         },
15325
15326         // private
15327         unregister : function(dlg){
15328             delete list[dlg.id];
15329             var i=0;
15330             var len=0;
15331             if(!accessList.indexOf){
15332                 for(  i = 0, len = accessList.length; i < len; i++){
15333                     if(accessList[i] == dlg){
15334                         accessList.splice(i, 1);
15335                         return;
15336                     }
15337                 }
15338             }else{
15339                  i = accessList.indexOf(dlg);
15340                 if(i != -1){
15341                     accessList.splice(i, 1);
15342                 }
15343             }
15344         },
15345
15346         /**
15347          * Gets a registered dialog by id
15348          * @param {String/Object} id The id of the dialog or a dialog
15349          * @return {Roo.BasicDialog} this
15350          */
15351         get : function(id){
15352             return typeof id == "object" ? id : list[id];
15353         },
15354
15355         /**
15356          * Brings the specified dialog to the front
15357          * @param {String/Object} dlg The id of the dialog or a dialog
15358          * @return {Roo.BasicDialog} this
15359          */
15360         bringToFront : function(dlg){
15361             dlg = this.get(dlg);
15362             if(dlg != front){
15363                 front = dlg;
15364                 dlg._lastAccess = new Date().getTime();
15365                 orderDialogs();
15366             }
15367             return dlg;
15368         },
15369
15370         /**
15371          * Sends the specified dialog to the back
15372          * @param {String/Object} dlg The id of the dialog or a dialog
15373          * @return {Roo.BasicDialog} this
15374          */
15375         sendToBack : function(dlg){
15376             dlg = this.get(dlg);
15377             dlg._lastAccess = -(new Date().getTime());
15378             orderDialogs();
15379             return dlg;
15380         },
15381
15382         /**
15383          * Hides all dialogs
15384          */
15385         hideAll : function(){
15386             for(var id in list){
15387                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15388                     list[id].hide();
15389                 }
15390             }
15391         }
15392     };
15393 }();
15394
15395 /**
15396  * @class Roo.LayoutDialog
15397  * @extends Roo.BasicDialog
15398  * Dialog which provides adjustments for working with a layout in a Dialog.
15399  * Add your necessary layout config options to the dialog's config.<br>
15400  * Example usage (including a nested layout):
15401  * <pre><code>
15402 if(!dialog){
15403     dialog = new Roo.LayoutDialog("download-dlg", {
15404         modal: true,
15405         width:600,
15406         height:450,
15407         shadow:true,
15408         minWidth:500,
15409         minHeight:350,
15410         autoTabs:true,
15411         proxyDrag:true,
15412         // layout config merges with the dialog config
15413         center:{
15414             tabPosition: "top",
15415             alwaysShowTabs: true
15416         }
15417     });
15418     dialog.addKeyListener(27, dialog.hide, dialog);
15419     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15420     dialog.addButton("Build It!", this.getDownload, this);
15421
15422     // we can even add nested layouts
15423     var innerLayout = new Roo.BorderLayout("dl-inner", {
15424         east: {
15425             initialSize: 200,
15426             autoScroll:true,
15427             split:true
15428         },
15429         center: {
15430             autoScroll:true
15431         }
15432     });
15433     innerLayout.beginUpdate();
15434     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15435     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15436     innerLayout.endUpdate(true);
15437
15438     var layout = dialog.getLayout();
15439     layout.beginUpdate();
15440     layout.add("center", new Roo.ContentPanel("standard-panel",
15441                         {title: "Download the Source", fitToFrame:true}));
15442     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15443                {title: "Build your own roo.js"}));
15444     layout.getRegion("center").showPanel(sp);
15445     layout.endUpdate();
15446 }
15447 </code></pre>
15448     * @constructor
15449     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15450     * @param {Object} config configuration options
15451   */
15452 Roo.LayoutDialog = function(el, cfg){
15453     
15454     var config=  cfg;
15455     if (typeof(cfg) == 'undefined') {
15456         config = Roo.apply({}, el);
15457         // not sure why we use documentElement here.. - it should always be body.
15458         // IE7 borks horribly if we use documentElement.
15459         // webkit also does not like documentElement - it creates a body element...
15460         el = Roo.get( document.body || document.documentElement ).createChild();
15461         //config.autoCreate = true;
15462     }
15463     
15464     
15465     config.autoTabs = false;
15466     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15467     this.body.setStyle({overflow:"hidden", position:"relative"});
15468     this.layout = new Roo.BorderLayout(this.body.dom, config);
15469     this.layout.monitorWindowResize = false;
15470     this.el.addClass("x-dlg-auto-layout");
15471     // fix case when center region overwrites center function
15472     this.center = Roo.BasicDialog.prototype.center;
15473     this.on("show", this.layout.layout, this.layout, true);
15474     if (config.items) {
15475         var xitems = config.items;
15476         delete config.items;
15477         Roo.each(xitems, this.addxtype, this);
15478     }
15479     
15480     
15481 };
15482 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15483     /**
15484      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15485      * @deprecated
15486      */
15487     endUpdate : function(){
15488         this.layout.endUpdate();
15489     },
15490
15491     /**
15492      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15493      *  @deprecated
15494      */
15495     beginUpdate : function(){
15496         this.layout.beginUpdate();
15497     },
15498
15499     /**
15500      * Get the BorderLayout for this dialog
15501      * @return {Roo.BorderLayout}
15502      */
15503     getLayout : function(){
15504         return this.layout;
15505     },
15506
15507     showEl : function(){
15508         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15509         if(Roo.isIE7){
15510             this.layout.layout();
15511         }
15512     },
15513
15514     // private
15515     // Use the syncHeightBeforeShow config option to control this automatically
15516     syncBodyHeight : function(){
15517         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15518         if(this.layout){this.layout.layout();}
15519     },
15520     
15521       /**
15522      * Add an xtype element (actually adds to the layout.)
15523      * @return {Object} xdata xtype object data.
15524      */
15525     
15526     addxtype : function(c) {
15527         return this.layout.addxtype(c);
15528     }
15529 });/*
15530  * Based on:
15531  * Ext JS Library 1.1.1
15532  * Copyright(c) 2006-2007, Ext JS, LLC.
15533  *
15534  * Originally Released Under LGPL - original licence link has changed is not relivant.
15535  *
15536  * Fork - LGPL
15537  * <script type="text/javascript">
15538  */
15539  
15540 /**
15541  * @class Roo.MessageBox
15542  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15543  * Example usage:
15544  *<pre><code>
15545 // Basic alert:
15546 Roo.Msg.alert('Status', 'Changes saved successfully.');
15547
15548 // Prompt for user data:
15549 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15550     if (btn == 'ok'){
15551         // process text value...
15552     }
15553 });
15554
15555 // Show a dialog using config options:
15556 Roo.Msg.show({
15557    title:'Save Changes?',
15558    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15559    buttons: Roo.Msg.YESNOCANCEL,
15560    fn: processResult,
15561    animEl: 'elId'
15562 });
15563 </code></pre>
15564  * @singleton
15565  */
15566 Roo.MessageBox = function(){
15567     var dlg, opt, mask, waitTimer;
15568     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15569     var buttons, activeTextEl, bwidth;
15570
15571     // private
15572     var handleButton = function(button){
15573         dlg.hide();
15574         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15575     };
15576
15577     // private
15578     var handleHide = function(){
15579         if(opt && opt.cls){
15580             dlg.el.removeClass(opt.cls);
15581         }
15582         if(waitTimer){
15583             Roo.TaskMgr.stop(waitTimer);
15584             waitTimer = null;
15585         }
15586     };
15587
15588     // private
15589     var updateButtons = function(b){
15590         var width = 0;
15591         if(!b){
15592             buttons["ok"].hide();
15593             buttons["cancel"].hide();
15594             buttons["yes"].hide();
15595             buttons["no"].hide();
15596             dlg.footer.dom.style.display = 'none';
15597             return width;
15598         }
15599         dlg.footer.dom.style.display = '';
15600         for(var k in buttons){
15601             if(typeof buttons[k] != "function"){
15602                 if(b[k]){
15603                     buttons[k].show();
15604                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15605                     width += buttons[k].el.getWidth()+15;
15606                 }else{
15607                     buttons[k].hide();
15608                 }
15609             }
15610         }
15611         return width;
15612     };
15613
15614     // private
15615     var handleEsc = function(d, k, e){
15616         if(opt && opt.closable !== false){
15617             dlg.hide();
15618         }
15619         if(e){
15620             e.stopEvent();
15621         }
15622     };
15623
15624     return {
15625         /**
15626          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15627          * @return {Roo.BasicDialog} The BasicDialog element
15628          */
15629         getDialog : function(){
15630            if(!dlg){
15631                 dlg = new Roo.BasicDialog("x-msg-box", {
15632                     autoCreate : true,
15633                     shadow: true,
15634                     draggable: true,
15635                     resizable:false,
15636                     constraintoviewport:false,
15637                     fixedcenter:true,
15638                     collapsible : false,
15639                     shim:true,
15640                     modal: true,
15641                     width:400, height:100,
15642                     buttonAlign:"center",
15643                     closeClick : function(){
15644                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15645                             handleButton("no");
15646                         }else{
15647                             handleButton("cancel");
15648                         }
15649                     }
15650                 });
15651                 dlg.on("hide", handleHide);
15652                 mask = dlg.mask;
15653                 dlg.addKeyListener(27, handleEsc);
15654                 buttons = {};
15655                 var bt = this.buttonText;
15656                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15657                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15658                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15659                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15660                 bodyEl = dlg.body.createChild({
15661
15662                     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>'
15663                 });
15664                 msgEl = bodyEl.dom.firstChild;
15665                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15666                 textboxEl.enableDisplayMode();
15667                 textboxEl.addKeyListener([10,13], function(){
15668                     if(dlg.isVisible() && opt && opt.buttons){
15669                         if(opt.buttons.ok){
15670                             handleButton("ok");
15671                         }else if(opt.buttons.yes){
15672                             handleButton("yes");
15673                         }
15674                     }
15675                 });
15676                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15677                 textareaEl.enableDisplayMode();
15678                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15679                 progressEl.enableDisplayMode();
15680                 var pf = progressEl.dom.firstChild;
15681                 if (pf) {
15682                     pp = Roo.get(pf.firstChild);
15683                     pp.setHeight(pf.offsetHeight);
15684                 }
15685                 
15686             }
15687             return dlg;
15688         },
15689
15690         /**
15691          * Updates the message box body text
15692          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15693          * the XHTML-compliant non-breaking space character '&amp;#160;')
15694          * @return {Roo.MessageBox} This message box
15695          */
15696         updateText : function(text){
15697             if(!dlg.isVisible() && !opt.width){
15698                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15699             }
15700             msgEl.innerHTML = text || '&#160;';
15701       
15702             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15703             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15704             var w = Math.max(
15705                     Math.min(opt.width || cw , this.maxWidth), 
15706                     Math.max(opt.minWidth || this.minWidth, bwidth)
15707             );
15708             if(opt.prompt){
15709                 activeTextEl.setWidth(w);
15710             }
15711             if(dlg.isVisible()){
15712                 dlg.fixedcenter = false;
15713             }
15714             // to big, make it scroll. = But as usual stupid IE does not support
15715             // !important..
15716             
15717             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15718                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15719                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15720             } else {
15721                 bodyEl.dom.style.height = '';
15722                 bodyEl.dom.style.overflowY = '';
15723             }
15724             if (cw > w) {
15725                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15726             } else {
15727                 bodyEl.dom.style.overflowX = '';
15728             }
15729             
15730             dlg.setContentSize(w, bodyEl.getHeight());
15731             if(dlg.isVisible()){
15732                 dlg.fixedcenter = true;
15733             }
15734             return this;
15735         },
15736
15737         /**
15738          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15739          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15740          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15741          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15742          * @return {Roo.MessageBox} This message box
15743          */
15744         updateProgress : function(value, text){
15745             if(text){
15746                 this.updateText(text);
15747             }
15748             if (pp) { // weird bug on my firefox - for some reason this is not defined
15749                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15750             }
15751             return this;
15752         },        
15753
15754         /**
15755          * Returns true if the message box is currently displayed
15756          * @return {Boolean} True if the message box is visible, else false
15757          */
15758         isVisible : function(){
15759             return dlg && dlg.isVisible();  
15760         },
15761
15762         /**
15763          * Hides the message box if it is displayed
15764          */
15765         hide : function(){
15766             if(this.isVisible()){
15767                 dlg.hide();
15768             }  
15769         },
15770
15771         /**
15772          * Displays a new message box, or reinitializes an existing message box, based on the config options
15773          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15774          * The following config object properties are supported:
15775          * <pre>
15776 Property    Type             Description
15777 ----------  ---------------  ------------------------------------------------------------------------------------
15778 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15779                                    closes (defaults to undefined)
15780 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15781                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15782 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15783                                    progress and wait dialogs will ignore this property and always hide the
15784                                    close button as they can only be closed programmatically.
15785 cls               String           A custom CSS class to apply to the message box element
15786 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15787                                    displayed (defaults to 75)
15788 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15789                                    function will be btn (the name of the button that was clicked, if applicable,
15790                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15791                                    Progress and wait dialogs will ignore this option since they do not respond to
15792                                    user actions and can only be closed programmatically, so any required function
15793                                    should be called by the same code after it closes the dialog.
15794 icon              String           A CSS class that provides a background image to be used as an icon for
15795                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15796 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15797 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15798 modal             Boolean          False to allow user interaction with the page while the message box is
15799                                    displayed (defaults to true)
15800 msg               String           A string that will replace the existing message box body text (defaults
15801                                    to the XHTML-compliant non-breaking space character '&#160;')
15802 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15803 progress          Boolean          True to display a progress bar (defaults to false)
15804 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15805 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15806 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15807 title             String           The title text
15808 value             String           The string value to set into the active textbox element if displayed
15809 wait              Boolean          True to display a progress bar (defaults to false)
15810 width             Number           The width of the dialog in pixels
15811 </pre>
15812          *
15813          * Example usage:
15814          * <pre><code>
15815 Roo.Msg.show({
15816    title: 'Address',
15817    msg: 'Please enter your address:',
15818    width: 300,
15819    buttons: Roo.MessageBox.OKCANCEL,
15820    multiline: true,
15821    fn: saveAddress,
15822    animEl: 'addAddressBtn'
15823 });
15824 </code></pre>
15825          * @param {Object} config Configuration options
15826          * @return {Roo.MessageBox} This message box
15827          */
15828         show : function(options)
15829         {
15830             
15831             // this causes nightmares if you show one dialog after another
15832             // especially on callbacks..
15833              
15834             if(this.isVisible()){
15835                 
15836                 this.hide();
15837                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15838                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15839                 Roo.log("New Dialog Message:" +  options.msg )
15840                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15841                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15842                 
15843             }
15844             var d = this.getDialog();
15845             opt = options;
15846             d.setTitle(opt.title || "&#160;");
15847             d.close.setDisplayed(opt.closable !== false);
15848             activeTextEl = textboxEl;
15849             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15850             if(opt.prompt){
15851                 if(opt.multiline){
15852                     textboxEl.hide();
15853                     textareaEl.show();
15854                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15855                         opt.multiline : this.defaultTextHeight);
15856                     activeTextEl = textareaEl;
15857                 }else{
15858                     textboxEl.show();
15859                     textareaEl.hide();
15860                 }
15861             }else{
15862                 textboxEl.hide();
15863                 textareaEl.hide();
15864             }
15865             progressEl.setDisplayed(opt.progress === true);
15866             this.updateProgress(0);
15867             activeTextEl.dom.value = opt.value || "";
15868             if(opt.prompt){
15869                 dlg.setDefaultButton(activeTextEl);
15870             }else{
15871                 var bs = opt.buttons;
15872                 var db = null;
15873                 if(bs && bs.ok){
15874                     db = buttons["ok"];
15875                 }else if(bs && bs.yes){
15876                     db = buttons["yes"];
15877                 }
15878                 dlg.setDefaultButton(db);
15879             }
15880             bwidth = updateButtons(opt.buttons);
15881             this.updateText(opt.msg);
15882             if(opt.cls){
15883                 d.el.addClass(opt.cls);
15884             }
15885             d.proxyDrag = opt.proxyDrag === true;
15886             d.modal = opt.modal !== false;
15887             d.mask = opt.modal !== false ? mask : false;
15888             if(!d.isVisible()){
15889                 // force it to the end of the z-index stack so it gets a cursor in FF
15890                 document.body.appendChild(dlg.el.dom);
15891                 d.animateTarget = null;
15892                 d.show(options.animEl);
15893             }
15894             return this;
15895         },
15896
15897         /**
15898          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15899          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15900          * and closing the message box when the process is complete.
15901          * @param {String} title The title bar text
15902          * @param {String} msg The message box body text
15903          * @return {Roo.MessageBox} This message box
15904          */
15905         progress : function(title, msg){
15906             this.show({
15907                 title : title,
15908                 msg : msg,
15909                 buttons: false,
15910                 progress:true,
15911                 closable:false,
15912                 minWidth: this.minProgressWidth,
15913                 modal : true
15914             });
15915             return this;
15916         },
15917
15918         /**
15919          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15920          * If a callback function is passed it will be called after the user clicks the button, and the
15921          * id of the button that was clicked will be passed as the only parameter to the callback
15922          * (could also be the top-right close button).
15923          * @param {String} title The title bar text
15924          * @param {String} msg The message box body text
15925          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15926          * @param {Object} scope (optional) The scope of the callback function
15927          * @return {Roo.MessageBox} This message box
15928          */
15929         alert : function(title, msg, fn, scope){
15930             this.show({
15931                 title : title,
15932                 msg : msg,
15933                 buttons: this.OK,
15934                 fn: fn,
15935                 scope : scope,
15936                 modal : true
15937             });
15938             return this;
15939         },
15940
15941         /**
15942          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15943          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15944          * You are responsible for closing the message box when the process is complete.
15945          * @param {String} msg The message box body text
15946          * @param {String} title (optional) The title bar text
15947          * @return {Roo.MessageBox} This message box
15948          */
15949         wait : function(msg, title){
15950             this.show({
15951                 title : title,
15952                 msg : msg,
15953                 buttons: false,
15954                 closable:false,
15955                 progress:true,
15956                 modal:true,
15957                 width:300,
15958                 wait:true
15959             });
15960             waitTimer = Roo.TaskMgr.start({
15961                 run: function(i){
15962                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15963                 },
15964                 interval: 1000
15965             });
15966             return this;
15967         },
15968
15969         /**
15970          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15971          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15972          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15973          * @param {String} title The title bar text
15974          * @param {String} msg The message box body text
15975          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15976          * @param {Object} scope (optional) The scope of the callback function
15977          * @return {Roo.MessageBox} This message box
15978          */
15979         confirm : function(title, msg, fn, scope){
15980             this.show({
15981                 title : title,
15982                 msg : msg,
15983                 buttons: this.YESNO,
15984                 fn: fn,
15985                 scope : scope,
15986                 modal : true
15987             });
15988             return this;
15989         },
15990
15991         /**
15992          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15993          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15994          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15995          * (could also be the top-right close button) and the text that was entered will be passed as the two
15996          * parameters to the callback.
15997          * @param {String} title The title bar text
15998          * @param {String} msg The message box body text
15999          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16000          * @param {Object} scope (optional) The scope of the callback function
16001          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16002          * property, or the height in pixels to create the textbox (defaults to false / single-line)
16003          * @return {Roo.MessageBox} This message box
16004          */
16005         prompt : function(title, msg, fn, scope, multiline){
16006             this.show({
16007                 title : title,
16008                 msg : msg,
16009                 buttons: this.OKCANCEL,
16010                 fn: fn,
16011                 minWidth:250,
16012                 scope : scope,
16013                 prompt:true,
16014                 multiline: multiline,
16015                 modal : true
16016             });
16017             return this;
16018         },
16019
16020         /**
16021          * Button config that displays a single OK button
16022          * @type Object
16023          */
16024         OK : {ok:true},
16025         /**
16026          * Button config that displays Yes and No buttons
16027          * @type Object
16028          */
16029         YESNO : {yes:true, no:true},
16030         /**
16031          * Button config that displays OK and Cancel buttons
16032          * @type Object
16033          */
16034         OKCANCEL : {ok:true, cancel:true},
16035         /**
16036          * Button config that displays Yes, No and Cancel buttons
16037          * @type Object
16038          */
16039         YESNOCANCEL : {yes:true, no:true, cancel:true},
16040
16041         /**
16042          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16043          * @type Number
16044          */
16045         defaultTextHeight : 75,
16046         /**
16047          * The maximum width in pixels of the message box (defaults to 600)
16048          * @type Number
16049          */
16050         maxWidth : 600,
16051         /**
16052          * The minimum width in pixels of the message box (defaults to 100)
16053          * @type Number
16054          */
16055         minWidth : 100,
16056         /**
16057          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16058          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16059          * @type Number
16060          */
16061         minProgressWidth : 250,
16062         /**
16063          * An object containing the default button text strings that can be overriden for localized language support.
16064          * Supported properties are: ok, cancel, yes and no.
16065          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16066          * @type Object
16067          */
16068         buttonText : {
16069             ok : "OK",
16070             cancel : "Cancel",
16071             yes : "Yes",
16072             no : "No"
16073         }
16074     };
16075 }();
16076
16077 /**
16078  * Shorthand for {@link Roo.MessageBox}
16079  */
16080 Roo.Msg = Roo.MessageBox;/*
16081  * Based on:
16082  * Ext JS Library 1.1.1
16083  * Copyright(c) 2006-2007, Ext JS, LLC.
16084  *
16085  * Originally Released Under LGPL - original licence link has changed is not relivant.
16086  *
16087  * Fork - LGPL
16088  * <script type="text/javascript">
16089  */
16090 /**
16091  * @class Roo.QuickTips
16092  * Provides attractive and customizable tooltips for any element.
16093  * @singleton
16094  */
16095 Roo.QuickTips = function(){
16096     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16097     var ce, bd, xy, dd;
16098     var visible = false, disabled = true, inited = false;
16099     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16100     
16101     var onOver = function(e){
16102         if(disabled){
16103             return;
16104         }
16105         var t = e.getTarget();
16106         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16107             return;
16108         }
16109         if(ce && t == ce.el){
16110             clearTimeout(hideProc);
16111             return;
16112         }
16113         if(t && tagEls[t.id]){
16114             tagEls[t.id].el = t;
16115             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16116             return;
16117         }
16118         var ttp, et = Roo.fly(t);
16119         var ns = cfg.namespace;
16120         if(tm.interceptTitles && t.title){
16121             ttp = t.title;
16122             t.qtip = ttp;
16123             t.removeAttribute("title");
16124             e.preventDefault();
16125         }else{
16126             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16127         }
16128         if(ttp){
16129             showProc = show.defer(tm.showDelay, tm, [{
16130                 el: t, 
16131                 text: ttp, 
16132                 width: et.getAttributeNS(ns, cfg.width),
16133                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16134                 title: et.getAttributeNS(ns, cfg.title),
16135                     cls: et.getAttributeNS(ns, cfg.cls)
16136             }]);
16137         }
16138     };
16139     
16140     var onOut = function(e){
16141         clearTimeout(showProc);
16142         var t = e.getTarget();
16143         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16144             hideProc = setTimeout(hide, tm.hideDelay);
16145         }
16146     };
16147     
16148     var onMove = function(e){
16149         if(disabled){
16150             return;
16151         }
16152         xy = e.getXY();
16153         xy[1] += 18;
16154         if(tm.trackMouse && ce){
16155             el.setXY(xy);
16156         }
16157     };
16158     
16159     var onDown = function(e){
16160         clearTimeout(showProc);
16161         clearTimeout(hideProc);
16162         if(!e.within(el)){
16163             if(tm.hideOnClick){
16164                 hide();
16165                 tm.disable();
16166                 tm.enable.defer(100, tm);
16167             }
16168         }
16169     };
16170     
16171     var getPad = function(){
16172         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16173     };
16174
16175     var show = function(o){
16176         if(disabled){
16177             return;
16178         }
16179         clearTimeout(dismissProc);
16180         ce = o;
16181         if(removeCls){ // in case manually hidden
16182             el.removeClass(removeCls);
16183             removeCls = null;
16184         }
16185         if(ce.cls){
16186             el.addClass(ce.cls);
16187             removeCls = ce.cls;
16188         }
16189         if(ce.title){
16190             tipTitle.update(ce.title);
16191             tipTitle.show();
16192         }else{
16193             tipTitle.update('');
16194             tipTitle.hide();
16195         }
16196         el.dom.style.width  = tm.maxWidth+'px';
16197         //tipBody.dom.style.width = '';
16198         tipBodyText.update(o.text);
16199         var p = getPad(), w = ce.width;
16200         if(!w){
16201             var td = tipBodyText.dom;
16202             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16203             if(aw > tm.maxWidth){
16204                 w = tm.maxWidth;
16205             }else if(aw < tm.minWidth){
16206                 w = tm.minWidth;
16207             }else{
16208                 w = aw;
16209             }
16210         }
16211         //tipBody.setWidth(w);
16212         el.setWidth(parseInt(w, 10) + p);
16213         if(ce.autoHide === false){
16214             close.setDisplayed(true);
16215             if(dd){
16216                 dd.unlock();
16217             }
16218         }else{
16219             close.setDisplayed(false);
16220             if(dd){
16221                 dd.lock();
16222             }
16223         }
16224         if(xy){
16225             el.avoidY = xy[1]-18;
16226             el.setXY(xy);
16227         }
16228         if(tm.animate){
16229             el.setOpacity(.1);
16230             el.setStyle("visibility", "visible");
16231             el.fadeIn({callback: afterShow});
16232         }else{
16233             afterShow();
16234         }
16235     };
16236     
16237     var afterShow = function(){
16238         if(ce){
16239             el.show();
16240             esc.enable();
16241             if(tm.autoDismiss && ce.autoHide !== false){
16242                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16243             }
16244         }
16245     };
16246     
16247     var hide = function(noanim){
16248         clearTimeout(dismissProc);
16249         clearTimeout(hideProc);
16250         ce = null;
16251         if(el.isVisible()){
16252             esc.disable();
16253             if(noanim !== true && tm.animate){
16254                 el.fadeOut({callback: afterHide});
16255             }else{
16256                 afterHide();
16257             } 
16258         }
16259     };
16260     
16261     var afterHide = function(){
16262         el.hide();
16263         if(removeCls){
16264             el.removeClass(removeCls);
16265             removeCls = null;
16266         }
16267     };
16268     
16269     return {
16270         /**
16271         * @cfg {Number} minWidth
16272         * The minimum width of the quick tip (defaults to 40)
16273         */
16274        minWidth : 40,
16275         /**
16276         * @cfg {Number} maxWidth
16277         * The maximum width of the quick tip (defaults to 300)
16278         */
16279        maxWidth : 300,
16280         /**
16281         * @cfg {Boolean} interceptTitles
16282         * True to automatically use the element's DOM title value if available (defaults to false)
16283         */
16284        interceptTitles : false,
16285         /**
16286         * @cfg {Boolean} trackMouse
16287         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16288         */
16289        trackMouse : false,
16290         /**
16291         * @cfg {Boolean} hideOnClick
16292         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16293         */
16294        hideOnClick : true,
16295         /**
16296         * @cfg {Number} showDelay
16297         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16298         */
16299        showDelay : 500,
16300         /**
16301         * @cfg {Number} hideDelay
16302         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16303         */
16304        hideDelay : 200,
16305         /**
16306         * @cfg {Boolean} autoHide
16307         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16308         * Used in conjunction with hideDelay.
16309         */
16310        autoHide : true,
16311         /**
16312         * @cfg {Boolean}
16313         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16314         * (defaults to true).  Used in conjunction with autoDismissDelay.
16315         */
16316        autoDismiss : true,
16317         /**
16318         * @cfg {Number}
16319         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16320         */
16321        autoDismissDelay : 5000,
16322        /**
16323         * @cfg {Boolean} animate
16324         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16325         */
16326        animate : false,
16327
16328        /**
16329         * @cfg {String} title
16330         * Title text to display (defaults to '').  This can be any valid HTML markup.
16331         */
16332         title: '',
16333        /**
16334         * @cfg {String} text
16335         * Body text to display (defaults to '').  This can be any valid HTML markup.
16336         */
16337         text : '',
16338        /**
16339         * @cfg {String} cls
16340         * A CSS class to apply to the base quick tip element (defaults to '').
16341         */
16342         cls : '',
16343        /**
16344         * @cfg {Number} width
16345         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16346         * minWidth or maxWidth.
16347         */
16348         width : null,
16349
16350     /**
16351      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16352      * or display QuickTips in a page.
16353      */
16354        init : function(){
16355           tm = Roo.QuickTips;
16356           cfg = tm.tagConfig;
16357           if(!inited){
16358               if(!Roo.isReady){ // allow calling of init() before onReady
16359                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16360                   return;
16361               }
16362               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16363               el.fxDefaults = {stopFx: true};
16364               // maximum custom styling
16365               //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>');
16366               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>');              
16367               tipTitle = el.child('h3');
16368               tipTitle.enableDisplayMode("block");
16369               tipBody = el.child('div.x-tip-bd');
16370               tipBodyText = el.child('div.x-tip-bd-inner');
16371               //bdLeft = el.child('div.x-tip-bd-left');
16372               //bdRight = el.child('div.x-tip-bd-right');
16373               close = el.child('div.x-tip-close');
16374               close.enableDisplayMode("block");
16375               close.on("click", hide);
16376               var d = Roo.get(document);
16377               d.on("mousedown", onDown);
16378               d.on("mouseover", onOver);
16379               d.on("mouseout", onOut);
16380               d.on("mousemove", onMove);
16381               esc = d.addKeyListener(27, hide);
16382               esc.disable();
16383               if(Roo.dd.DD){
16384                   dd = el.initDD("default", null, {
16385                       onDrag : function(){
16386                           el.sync();  
16387                       }
16388                   });
16389                   dd.setHandleElId(tipTitle.id);
16390                   dd.lock();
16391               }
16392               inited = true;
16393           }
16394           this.enable(); 
16395        },
16396
16397     /**
16398      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16399      * are supported:
16400      * <pre>
16401 Property    Type                   Description
16402 ----------  ---------------------  ------------------------------------------------------------------------
16403 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16404      * </ul>
16405      * @param {Object} config The config object
16406      */
16407        register : function(config){
16408            var cs = config instanceof Array ? config : arguments;
16409            for(var i = 0, len = cs.length; i < len; i++) {
16410                var c = cs[i];
16411                var target = c.target;
16412                if(target){
16413                    if(target instanceof Array){
16414                        for(var j = 0, jlen = target.length; j < jlen; j++){
16415                            tagEls[target[j]] = c;
16416                        }
16417                    }else{
16418                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16419                    }
16420                }
16421            }
16422        },
16423
16424     /**
16425      * Removes this quick tip from its element and destroys it.
16426      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16427      */
16428        unregister : function(el){
16429            delete tagEls[Roo.id(el)];
16430        },
16431
16432     /**
16433      * Enable this quick tip.
16434      */
16435        enable : function(){
16436            if(inited && disabled){
16437                locks.pop();
16438                if(locks.length < 1){
16439                    disabled = false;
16440                }
16441            }
16442        },
16443
16444     /**
16445      * Disable this quick tip.
16446      */
16447        disable : function(){
16448           disabled = true;
16449           clearTimeout(showProc);
16450           clearTimeout(hideProc);
16451           clearTimeout(dismissProc);
16452           if(ce){
16453               hide(true);
16454           }
16455           locks.push(1);
16456        },
16457
16458     /**
16459      * Returns true if the quick tip is enabled, else false.
16460      */
16461        isEnabled : function(){
16462             return !disabled;
16463        },
16464
16465         // private
16466        tagConfig : {
16467            namespace : "ext",
16468            attribute : "qtip",
16469            width : "width",
16470            target : "target",
16471            title : "qtitle",
16472            hide : "hide",
16473            cls : "qclass"
16474        }
16475    };
16476 }();
16477
16478 // backwards compat
16479 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16480  * Based on:
16481  * Ext JS Library 1.1.1
16482  * Copyright(c) 2006-2007, Ext JS, LLC.
16483  *
16484  * Originally Released Under LGPL - original licence link has changed is not relivant.
16485  *
16486  * Fork - LGPL
16487  * <script type="text/javascript">
16488  */
16489  
16490
16491 /**
16492  * @class Roo.tree.TreePanel
16493  * @extends Roo.data.Tree
16494
16495  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16496  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16497  * @cfg {Boolean} enableDD true to enable drag and drop
16498  * @cfg {Boolean} enableDrag true to enable just drag
16499  * @cfg {Boolean} enableDrop true to enable just drop
16500  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16501  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16502  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16503  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16504  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16505  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16506  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16507  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16508  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16509  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16510  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16511  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16512  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16513  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16514  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16515  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16516  * 
16517  * @constructor
16518  * @param {String/HTMLElement/Element} el The container element
16519  * @param {Object} config
16520  */
16521 Roo.tree.TreePanel = function(el, config){
16522     var root = false;
16523     var loader = false;
16524     if (config.root) {
16525         root = config.root;
16526         delete config.root;
16527     }
16528     if (config.loader) {
16529         loader = config.loader;
16530         delete config.loader;
16531     }
16532     
16533     Roo.apply(this, config);
16534     Roo.tree.TreePanel.superclass.constructor.call(this);
16535     this.el = Roo.get(el);
16536     this.el.addClass('x-tree');
16537     //console.log(root);
16538     if (root) {
16539         this.setRootNode( Roo.factory(root, Roo.tree));
16540     }
16541     if (loader) {
16542         this.loader = Roo.factory(loader, Roo.tree);
16543     }
16544    /**
16545     * Read-only. The id of the container element becomes this TreePanel's id.
16546     */
16547     this.id = this.el.id;
16548     this.addEvents({
16549         /**
16550         * @event beforeload
16551         * Fires before a node is loaded, return false to cancel
16552         * @param {Node} node The node being loaded
16553         */
16554         "beforeload" : true,
16555         /**
16556         * @event load
16557         * Fires when a node is loaded
16558         * @param {Node} node The node that was loaded
16559         */
16560         "load" : true,
16561         /**
16562         * @event textchange
16563         * Fires when the text for a node is changed
16564         * @param {Node} node The node
16565         * @param {String} text The new text
16566         * @param {String} oldText The old text
16567         */
16568         "textchange" : true,
16569         /**
16570         * @event beforeexpand
16571         * Fires before a node is expanded, return false to cancel.
16572         * @param {Node} node The node
16573         * @param {Boolean} deep
16574         * @param {Boolean} anim
16575         */
16576         "beforeexpand" : true,
16577         /**
16578         * @event beforecollapse
16579         * Fires before a node is collapsed, return false to cancel.
16580         * @param {Node} node The node
16581         * @param {Boolean} deep
16582         * @param {Boolean} anim
16583         */
16584         "beforecollapse" : true,
16585         /**
16586         * @event expand
16587         * Fires when a node is expanded
16588         * @param {Node} node The node
16589         */
16590         "expand" : true,
16591         /**
16592         * @event disabledchange
16593         * Fires when the disabled status of a node changes
16594         * @param {Node} node The node
16595         * @param {Boolean} disabled
16596         */
16597         "disabledchange" : true,
16598         /**
16599         * @event collapse
16600         * Fires when a node is collapsed
16601         * @param {Node} node The node
16602         */
16603         "collapse" : true,
16604         /**
16605         * @event beforeclick
16606         * Fires before click processing on a node. Return false to cancel the default action.
16607         * @param {Node} node The node
16608         * @param {Roo.EventObject} e The event object
16609         */
16610         "beforeclick":true,
16611         /**
16612         * @event checkchange
16613         * Fires when a node with a checkbox's checked property changes
16614         * @param {Node} this This node
16615         * @param {Boolean} checked
16616         */
16617         "checkchange":true,
16618         /**
16619         * @event click
16620         * Fires when a node is clicked
16621         * @param {Node} node The node
16622         * @param {Roo.EventObject} e The event object
16623         */
16624         "click":true,
16625         /**
16626         * @event dblclick
16627         * Fires when a node is double clicked
16628         * @param {Node} node The node
16629         * @param {Roo.EventObject} e The event object
16630         */
16631         "dblclick":true,
16632         /**
16633         * @event contextmenu
16634         * Fires when a node is right clicked
16635         * @param {Node} node The node
16636         * @param {Roo.EventObject} e The event object
16637         */
16638         "contextmenu":true,
16639         /**
16640         * @event beforechildrenrendered
16641         * Fires right before the child nodes for a node are rendered
16642         * @param {Node} node The node
16643         */
16644         "beforechildrenrendered":true,
16645         /**
16646         * @event startdrag
16647         * Fires when a node starts being dragged
16648         * @param {Roo.tree.TreePanel} this
16649         * @param {Roo.tree.TreeNode} node
16650         * @param {event} e The raw browser event
16651         */ 
16652        "startdrag" : true,
16653        /**
16654         * @event enddrag
16655         * Fires when a drag operation is complete
16656         * @param {Roo.tree.TreePanel} this
16657         * @param {Roo.tree.TreeNode} node
16658         * @param {event} e The raw browser event
16659         */
16660        "enddrag" : true,
16661        /**
16662         * @event dragdrop
16663         * Fires when a dragged node is dropped on a valid DD target
16664         * @param {Roo.tree.TreePanel} this
16665         * @param {Roo.tree.TreeNode} node
16666         * @param {DD} dd The dd it was dropped on
16667         * @param {event} e The raw browser event
16668         */
16669        "dragdrop" : true,
16670        /**
16671         * @event beforenodedrop
16672         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16673         * passed to handlers has the following properties:<br />
16674         * <ul style="padding:5px;padding-left:16px;">
16675         * <li>tree - The TreePanel</li>
16676         * <li>target - The node being targeted for the drop</li>
16677         * <li>data - The drag data from the drag source</li>
16678         * <li>point - The point of the drop - append, above or below</li>
16679         * <li>source - The drag source</li>
16680         * <li>rawEvent - Raw mouse event</li>
16681         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16682         * to be inserted by setting them on this object.</li>
16683         * <li>cancel - Set this to true to cancel the drop.</li>
16684         * </ul>
16685         * @param {Object} dropEvent
16686         */
16687        "beforenodedrop" : true,
16688        /**
16689         * @event nodedrop
16690         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16691         * passed to handlers has the following properties:<br />
16692         * <ul style="padding:5px;padding-left:16px;">
16693         * <li>tree - The TreePanel</li>
16694         * <li>target - The node being targeted for the drop</li>
16695         * <li>data - The drag data from the drag source</li>
16696         * <li>point - The point of the drop - append, above or below</li>
16697         * <li>source - The drag source</li>
16698         * <li>rawEvent - Raw mouse event</li>
16699         * <li>dropNode - Dropped node(s).</li>
16700         * </ul>
16701         * @param {Object} dropEvent
16702         */
16703        "nodedrop" : true,
16704         /**
16705         * @event nodedragover
16706         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16707         * passed to handlers has the following properties:<br />
16708         * <ul style="padding:5px;padding-left:16px;">
16709         * <li>tree - The TreePanel</li>
16710         * <li>target - The node being targeted for the drop</li>
16711         * <li>data - The drag data from the drag source</li>
16712         * <li>point - The point of the drop - append, above or below</li>
16713         * <li>source - The drag source</li>
16714         * <li>rawEvent - Raw mouse event</li>
16715         * <li>dropNode - Drop node(s) provided by the source.</li>
16716         * <li>cancel - Set this to true to signal drop not allowed.</li>
16717         * </ul>
16718         * @param {Object} dragOverEvent
16719         */
16720        "nodedragover" : true
16721         
16722     });
16723     if(this.singleExpand){
16724        this.on("beforeexpand", this.restrictExpand, this);
16725     }
16726     if (this.editor) {
16727         this.editor.tree = this;
16728         this.editor = Roo.factory(this.editor, Roo.tree);
16729     }
16730     
16731     if (this.selModel) {
16732         this.selModel = Roo.factory(this.selModel, Roo.tree);
16733     }
16734    
16735 };
16736 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16737     rootVisible : true,
16738     animate: Roo.enableFx,
16739     lines : true,
16740     enableDD : false,
16741     hlDrop : Roo.enableFx,
16742   
16743     renderer: false,
16744     
16745     rendererTip: false,
16746     // private
16747     restrictExpand : function(node){
16748         var p = node.parentNode;
16749         if(p){
16750             if(p.expandedChild && p.expandedChild.parentNode == p){
16751                 p.expandedChild.collapse();
16752             }
16753             p.expandedChild = node;
16754         }
16755     },
16756
16757     // private override
16758     setRootNode : function(node){
16759         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16760         if(!this.rootVisible){
16761             node.ui = new Roo.tree.RootTreeNodeUI(node);
16762         }
16763         return node;
16764     },
16765
16766     /**
16767      * Returns the container element for this TreePanel
16768      */
16769     getEl : function(){
16770         return this.el;
16771     },
16772
16773     /**
16774      * Returns the default TreeLoader for this TreePanel
16775      */
16776     getLoader : function(){
16777         return this.loader;
16778     },
16779
16780     /**
16781      * Expand all nodes
16782      */
16783     expandAll : function(){
16784         this.root.expand(true);
16785     },
16786
16787     /**
16788      * Collapse all nodes
16789      */
16790     collapseAll : function(){
16791         this.root.collapse(true);
16792     },
16793
16794     /**
16795      * Returns the selection model used by this TreePanel
16796      */
16797     getSelectionModel : function(){
16798         if(!this.selModel){
16799             this.selModel = new Roo.tree.DefaultSelectionModel();
16800         }
16801         return this.selModel;
16802     },
16803
16804     /**
16805      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16806      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16807      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16808      * @return {Array}
16809      */
16810     getChecked : function(a, startNode){
16811         startNode = startNode || this.root;
16812         var r = [];
16813         var f = function(){
16814             if(this.attributes.checked){
16815                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16816             }
16817         }
16818         startNode.cascade(f);
16819         return r;
16820     },
16821
16822     /**
16823      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16824      * @param {String} path
16825      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16826      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16827      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16828      */
16829     expandPath : function(path, attr, callback){
16830         attr = attr || "id";
16831         var keys = path.split(this.pathSeparator);
16832         var curNode = this.root;
16833         if(curNode.attributes[attr] != keys[1]){ // invalid root
16834             if(callback){
16835                 callback(false, null);
16836             }
16837             return;
16838         }
16839         var index = 1;
16840         var f = function(){
16841             if(++index == keys.length){
16842                 if(callback){
16843                     callback(true, curNode);
16844                 }
16845                 return;
16846             }
16847             var c = curNode.findChild(attr, keys[index]);
16848             if(!c){
16849                 if(callback){
16850                     callback(false, curNode);
16851                 }
16852                 return;
16853             }
16854             curNode = c;
16855             c.expand(false, false, f);
16856         };
16857         curNode.expand(false, false, f);
16858     },
16859
16860     /**
16861      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16862      * @param {String} path
16863      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16864      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16865      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16866      */
16867     selectPath : function(path, attr, callback){
16868         attr = attr || "id";
16869         var keys = path.split(this.pathSeparator);
16870         var v = keys.pop();
16871         if(keys.length > 0){
16872             var f = function(success, node){
16873                 if(success && node){
16874                     var n = node.findChild(attr, v);
16875                     if(n){
16876                         n.select();
16877                         if(callback){
16878                             callback(true, n);
16879                         }
16880                     }else if(callback){
16881                         callback(false, n);
16882                     }
16883                 }else{
16884                     if(callback){
16885                         callback(false, n);
16886                     }
16887                 }
16888             };
16889             this.expandPath(keys.join(this.pathSeparator), attr, f);
16890         }else{
16891             this.root.select();
16892             if(callback){
16893                 callback(true, this.root);
16894             }
16895         }
16896     },
16897
16898     getTreeEl : function(){
16899         return this.el;
16900     },
16901
16902     /**
16903      * Trigger rendering of this TreePanel
16904      */
16905     render : function(){
16906         if (this.innerCt) {
16907             return this; // stop it rendering more than once!!
16908         }
16909         
16910         this.innerCt = this.el.createChild({tag:"ul",
16911                cls:"x-tree-root-ct " +
16912                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16913
16914         if(this.containerScroll){
16915             Roo.dd.ScrollManager.register(this.el);
16916         }
16917         if((this.enableDD || this.enableDrop) && !this.dropZone){
16918            /**
16919             * The dropZone used by this tree if drop is enabled
16920             * @type Roo.tree.TreeDropZone
16921             */
16922              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16923                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16924            });
16925         }
16926         if((this.enableDD || this.enableDrag) && !this.dragZone){
16927            /**
16928             * The dragZone used by this tree if drag is enabled
16929             * @type Roo.tree.TreeDragZone
16930             */
16931             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16932                ddGroup: this.ddGroup || "TreeDD",
16933                scroll: this.ddScroll
16934            });
16935         }
16936         this.getSelectionModel().init(this);
16937         if (!this.root) {
16938             Roo.log("ROOT not set in tree");
16939             return this;
16940         }
16941         this.root.render();
16942         if(!this.rootVisible){
16943             this.root.renderChildren();
16944         }
16945         return this;
16946     }
16947 });/*
16948  * Based on:
16949  * Ext JS Library 1.1.1
16950  * Copyright(c) 2006-2007, Ext JS, LLC.
16951  *
16952  * Originally Released Under LGPL - original licence link has changed is not relivant.
16953  *
16954  * Fork - LGPL
16955  * <script type="text/javascript">
16956  */
16957  
16958
16959 /**
16960  * @class Roo.tree.DefaultSelectionModel
16961  * @extends Roo.util.Observable
16962  * The default single selection for a TreePanel.
16963  * @param {Object} cfg Configuration
16964  */
16965 Roo.tree.DefaultSelectionModel = function(cfg){
16966    this.selNode = null;
16967    
16968    
16969    
16970    this.addEvents({
16971        /**
16972         * @event selectionchange
16973         * Fires when the selected node changes
16974         * @param {DefaultSelectionModel} this
16975         * @param {TreeNode} node the new selection
16976         */
16977        "selectionchange" : true,
16978
16979        /**
16980         * @event beforeselect
16981         * Fires before the selected node changes, return false to cancel the change
16982         * @param {DefaultSelectionModel} this
16983         * @param {TreeNode} node the new selection
16984         * @param {TreeNode} node the old selection
16985         */
16986        "beforeselect" : true
16987    });
16988    
16989     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16990 };
16991
16992 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16993     init : function(tree){
16994         this.tree = tree;
16995         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16996         tree.on("click", this.onNodeClick, this);
16997     },
16998     
16999     onNodeClick : function(node, e){
17000         if (e.ctrlKey && this.selNode == node)  {
17001             this.unselect(node);
17002             return;
17003         }
17004         this.select(node);
17005     },
17006     
17007     /**
17008      * Select a node.
17009      * @param {TreeNode} node The node to select
17010      * @return {TreeNode} The selected node
17011      */
17012     select : function(node){
17013         var last = this.selNode;
17014         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17015             if(last){
17016                 last.ui.onSelectedChange(false);
17017             }
17018             this.selNode = node;
17019             node.ui.onSelectedChange(true);
17020             this.fireEvent("selectionchange", this, node, last);
17021         }
17022         return node;
17023     },
17024     
17025     /**
17026      * Deselect a node.
17027      * @param {TreeNode} node The node to unselect
17028      */
17029     unselect : function(node){
17030         if(this.selNode == node){
17031             this.clearSelections();
17032         }    
17033     },
17034     
17035     /**
17036      * Clear all selections
17037      */
17038     clearSelections : function(){
17039         var n = this.selNode;
17040         if(n){
17041             n.ui.onSelectedChange(false);
17042             this.selNode = null;
17043             this.fireEvent("selectionchange", this, null);
17044         }
17045         return n;
17046     },
17047     
17048     /**
17049      * Get the selected node
17050      * @return {TreeNode} The selected node
17051      */
17052     getSelectedNode : function(){
17053         return this.selNode;    
17054     },
17055     
17056     /**
17057      * Returns true if the node is selected
17058      * @param {TreeNode} node The node to check
17059      * @return {Boolean}
17060      */
17061     isSelected : function(node){
17062         return this.selNode == node;  
17063     },
17064
17065     /**
17066      * Selects the node above the selected node in the tree, intelligently walking the nodes
17067      * @return TreeNode The new selection
17068      */
17069     selectPrevious : function(){
17070         var s = this.selNode || this.lastSelNode;
17071         if(!s){
17072             return null;
17073         }
17074         var ps = s.previousSibling;
17075         if(ps){
17076             if(!ps.isExpanded() || ps.childNodes.length < 1){
17077                 return this.select(ps);
17078             } else{
17079                 var lc = ps.lastChild;
17080                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17081                     lc = lc.lastChild;
17082                 }
17083                 return this.select(lc);
17084             }
17085         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17086             return this.select(s.parentNode);
17087         }
17088         return null;
17089     },
17090
17091     /**
17092      * Selects the node above the selected node in the tree, intelligently walking the nodes
17093      * @return TreeNode The new selection
17094      */
17095     selectNext : function(){
17096         var s = this.selNode || this.lastSelNode;
17097         if(!s){
17098             return null;
17099         }
17100         if(s.firstChild && s.isExpanded()){
17101              return this.select(s.firstChild);
17102          }else if(s.nextSibling){
17103              return this.select(s.nextSibling);
17104          }else if(s.parentNode){
17105             var newS = null;
17106             s.parentNode.bubble(function(){
17107                 if(this.nextSibling){
17108                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17109                     return false;
17110                 }
17111             });
17112             return newS;
17113          }
17114         return null;
17115     },
17116
17117     onKeyDown : function(e){
17118         var s = this.selNode || this.lastSelNode;
17119         // undesirable, but required
17120         var sm = this;
17121         if(!s){
17122             return;
17123         }
17124         var k = e.getKey();
17125         switch(k){
17126              case e.DOWN:
17127                  e.stopEvent();
17128                  this.selectNext();
17129              break;
17130              case e.UP:
17131                  e.stopEvent();
17132                  this.selectPrevious();
17133              break;
17134              case e.RIGHT:
17135                  e.preventDefault();
17136                  if(s.hasChildNodes()){
17137                      if(!s.isExpanded()){
17138                          s.expand();
17139                      }else if(s.firstChild){
17140                          this.select(s.firstChild, e);
17141                      }
17142                  }
17143              break;
17144              case e.LEFT:
17145                  e.preventDefault();
17146                  if(s.hasChildNodes() && s.isExpanded()){
17147                      s.collapse();
17148                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17149                      this.select(s.parentNode, e);
17150                  }
17151              break;
17152         };
17153     }
17154 });
17155
17156 /**
17157  * @class Roo.tree.MultiSelectionModel
17158  * @extends Roo.util.Observable
17159  * Multi selection for a TreePanel.
17160  * @param {Object} cfg Configuration
17161  */
17162 Roo.tree.MultiSelectionModel = function(){
17163    this.selNodes = [];
17164    this.selMap = {};
17165    this.addEvents({
17166        /**
17167         * @event selectionchange
17168         * Fires when the selected nodes change
17169         * @param {MultiSelectionModel} this
17170         * @param {Array} nodes Array of the selected nodes
17171         */
17172        "selectionchange" : true
17173    });
17174    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17175    
17176 };
17177
17178 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17179     init : function(tree){
17180         this.tree = tree;
17181         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17182         tree.on("click", this.onNodeClick, this);
17183     },
17184     
17185     onNodeClick : function(node, e){
17186         this.select(node, e, e.ctrlKey);
17187     },
17188     
17189     /**
17190      * Select a node.
17191      * @param {TreeNode} node The node to select
17192      * @param {EventObject} e (optional) An event associated with the selection
17193      * @param {Boolean} keepExisting True to retain existing selections
17194      * @return {TreeNode} The selected node
17195      */
17196     select : function(node, e, keepExisting){
17197         if(keepExisting !== true){
17198             this.clearSelections(true);
17199         }
17200         if(this.isSelected(node)){
17201             this.lastSelNode = node;
17202             return node;
17203         }
17204         this.selNodes.push(node);
17205         this.selMap[node.id] = node;
17206         this.lastSelNode = node;
17207         node.ui.onSelectedChange(true);
17208         this.fireEvent("selectionchange", this, this.selNodes);
17209         return node;
17210     },
17211     
17212     /**
17213      * Deselect a node.
17214      * @param {TreeNode} node The node to unselect
17215      */
17216     unselect : function(node){
17217         if(this.selMap[node.id]){
17218             node.ui.onSelectedChange(false);
17219             var sn = this.selNodes;
17220             var index = -1;
17221             if(sn.indexOf){
17222                 index = sn.indexOf(node);
17223             }else{
17224                 for(var i = 0, len = sn.length; i < len; i++){
17225                     if(sn[i] == node){
17226                         index = i;
17227                         break;
17228                     }
17229                 }
17230             }
17231             if(index != -1){
17232                 this.selNodes.splice(index, 1);
17233             }
17234             delete this.selMap[node.id];
17235             this.fireEvent("selectionchange", this, this.selNodes);
17236         }
17237     },
17238     
17239     /**
17240      * Clear all selections
17241      */
17242     clearSelections : function(suppressEvent){
17243         var sn = this.selNodes;
17244         if(sn.length > 0){
17245             for(var i = 0, len = sn.length; i < len; i++){
17246                 sn[i].ui.onSelectedChange(false);
17247             }
17248             this.selNodes = [];
17249             this.selMap = {};
17250             if(suppressEvent !== true){
17251                 this.fireEvent("selectionchange", this, this.selNodes);
17252             }
17253         }
17254     },
17255     
17256     /**
17257      * Returns true if the node is selected
17258      * @param {TreeNode} node The node to check
17259      * @return {Boolean}
17260      */
17261     isSelected : function(node){
17262         return this.selMap[node.id] ? true : false;  
17263     },
17264     
17265     /**
17266      * Returns an array of the selected nodes
17267      * @return {Array}
17268      */
17269     getSelectedNodes : function(){
17270         return this.selNodes;    
17271     },
17272
17273     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17274
17275     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17276
17277     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17278 });/*
17279  * Based on:
17280  * Ext JS Library 1.1.1
17281  * Copyright(c) 2006-2007, Ext JS, LLC.
17282  *
17283  * Originally Released Under LGPL - original licence link has changed is not relivant.
17284  *
17285  * Fork - LGPL
17286  * <script type="text/javascript">
17287  */
17288  
17289 /**
17290  * @class Roo.tree.TreeNode
17291  * @extends Roo.data.Node
17292  * @cfg {String} text The text for this node
17293  * @cfg {Boolean} expanded true to start the node expanded
17294  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17295  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17296  * @cfg {Boolean} disabled true to start the node disabled
17297  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17298  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17299  * @cfg {String} cls A css class to be added to the node
17300  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17301  * @cfg {String} href URL of the link used for the node (defaults to #)
17302  * @cfg {String} hrefTarget target frame for the link
17303  * @cfg {String} qtip An Ext QuickTip for the node
17304  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17305  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17306  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17307  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17308  * (defaults to undefined with no checkbox rendered)
17309  * @constructor
17310  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17311  */
17312 Roo.tree.TreeNode = function(attributes){
17313     attributes = attributes || {};
17314     if(typeof attributes == "string"){
17315         attributes = {text: attributes};
17316     }
17317     this.childrenRendered = false;
17318     this.rendered = false;
17319     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17320     this.expanded = attributes.expanded === true;
17321     this.isTarget = attributes.isTarget !== false;
17322     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17323     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17324
17325     /**
17326      * Read-only. The text for this node. To change it use setText().
17327      * @type String
17328      */
17329     this.text = attributes.text;
17330     /**
17331      * True if this node is disabled.
17332      * @type Boolean
17333      */
17334     this.disabled = attributes.disabled === true;
17335
17336     this.addEvents({
17337         /**
17338         * @event textchange
17339         * Fires when the text for this node is changed
17340         * @param {Node} this This node
17341         * @param {String} text The new text
17342         * @param {String} oldText The old text
17343         */
17344         "textchange" : true,
17345         /**
17346         * @event beforeexpand
17347         * Fires before this node is expanded, return false to cancel.
17348         * @param {Node} this This node
17349         * @param {Boolean} deep
17350         * @param {Boolean} anim
17351         */
17352         "beforeexpand" : true,
17353         /**
17354         * @event beforecollapse
17355         * Fires before this node is collapsed, return false to cancel.
17356         * @param {Node} this This node
17357         * @param {Boolean} deep
17358         * @param {Boolean} anim
17359         */
17360         "beforecollapse" : true,
17361         /**
17362         * @event expand
17363         * Fires when this node is expanded
17364         * @param {Node} this This node
17365         */
17366         "expand" : true,
17367         /**
17368         * @event disabledchange
17369         * Fires when the disabled status of this node changes
17370         * @param {Node} this This node
17371         * @param {Boolean} disabled
17372         */
17373         "disabledchange" : true,
17374         /**
17375         * @event collapse
17376         * Fires when this node is collapsed
17377         * @param {Node} this This node
17378         */
17379         "collapse" : true,
17380         /**
17381         * @event beforeclick
17382         * Fires before click processing. Return false to cancel the default action.
17383         * @param {Node} this This node
17384         * @param {Roo.EventObject} e The event object
17385         */
17386         "beforeclick":true,
17387         /**
17388         * @event checkchange
17389         * Fires when a node with a checkbox's checked property changes
17390         * @param {Node} this This node
17391         * @param {Boolean} checked
17392         */
17393         "checkchange":true,
17394         /**
17395         * @event click
17396         * Fires when this node is clicked
17397         * @param {Node} this This node
17398         * @param {Roo.EventObject} e The event object
17399         */
17400         "click":true,
17401         /**
17402         * @event dblclick
17403         * Fires when this node is double clicked
17404         * @param {Node} this This node
17405         * @param {Roo.EventObject} e The event object
17406         */
17407         "dblclick":true,
17408         /**
17409         * @event contextmenu
17410         * Fires when this node is right clicked
17411         * @param {Node} this This node
17412         * @param {Roo.EventObject} e The event object
17413         */
17414         "contextmenu":true,
17415         /**
17416         * @event beforechildrenrendered
17417         * Fires right before the child nodes for this node are rendered
17418         * @param {Node} this This node
17419         */
17420         "beforechildrenrendered":true
17421     });
17422
17423     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17424
17425     /**
17426      * Read-only. The UI for this node
17427      * @type TreeNodeUI
17428      */
17429     this.ui = new uiClass(this);
17430     
17431     // finally support items[]
17432     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17433         return;
17434     }
17435     
17436     
17437     Roo.each(this.attributes.items, function(c) {
17438         this.appendChild(Roo.factory(c,Roo.Tree));
17439     }, this);
17440     delete this.attributes.items;
17441     
17442     
17443     
17444 };
17445 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17446     preventHScroll: true,
17447     /**
17448      * Returns true if this node is expanded
17449      * @return {Boolean}
17450      */
17451     isExpanded : function(){
17452         return this.expanded;
17453     },
17454
17455     /**
17456      * Returns the UI object for this node
17457      * @return {TreeNodeUI}
17458      */
17459     getUI : function(){
17460         return this.ui;
17461     },
17462
17463     // private override
17464     setFirstChild : function(node){
17465         var of = this.firstChild;
17466         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17467         if(this.childrenRendered && of && node != of){
17468             of.renderIndent(true, true);
17469         }
17470         if(this.rendered){
17471             this.renderIndent(true, true);
17472         }
17473     },
17474
17475     // private override
17476     setLastChild : function(node){
17477         var ol = this.lastChild;
17478         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17479         if(this.childrenRendered && ol && node != ol){
17480             ol.renderIndent(true, true);
17481         }
17482         if(this.rendered){
17483             this.renderIndent(true, true);
17484         }
17485     },
17486
17487     // these methods are overridden to provide lazy rendering support
17488     // private override
17489     appendChild : function()
17490     {
17491         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17492         if(node && this.childrenRendered){
17493             node.render();
17494         }
17495         this.ui.updateExpandIcon();
17496         return node;
17497     },
17498
17499     // private override
17500     removeChild : function(node){
17501         this.ownerTree.getSelectionModel().unselect(node);
17502         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17503         // if it's been rendered remove dom node
17504         if(this.childrenRendered){
17505             node.ui.remove();
17506         }
17507         if(this.childNodes.length < 1){
17508             this.collapse(false, false);
17509         }else{
17510             this.ui.updateExpandIcon();
17511         }
17512         if(!this.firstChild) {
17513             this.childrenRendered = false;
17514         }
17515         return node;
17516     },
17517
17518     // private override
17519     insertBefore : function(node, refNode){
17520         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17521         if(newNode && refNode && this.childrenRendered){
17522             node.render();
17523         }
17524         this.ui.updateExpandIcon();
17525         return newNode;
17526     },
17527
17528     /**
17529      * Sets the text for this node
17530      * @param {String} text
17531      */
17532     setText : function(text){
17533         var oldText = this.text;
17534         this.text = text;
17535         this.attributes.text = text;
17536         if(this.rendered){ // event without subscribing
17537             this.ui.onTextChange(this, text, oldText);
17538         }
17539         this.fireEvent("textchange", this, text, oldText);
17540     },
17541
17542     /**
17543      * Triggers selection of this node
17544      */
17545     select : function(){
17546         this.getOwnerTree().getSelectionModel().select(this);
17547     },
17548
17549     /**
17550      * Triggers deselection of this node
17551      */
17552     unselect : function(){
17553         this.getOwnerTree().getSelectionModel().unselect(this);
17554     },
17555
17556     /**
17557      * Returns true if this node is selected
17558      * @return {Boolean}
17559      */
17560     isSelected : function(){
17561         return this.getOwnerTree().getSelectionModel().isSelected(this);
17562     },
17563
17564     /**
17565      * Expand this node.
17566      * @param {Boolean} deep (optional) True to expand all children as well
17567      * @param {Boolean} anim (optional) false to cancel the default animation
17568      * @param {Function} callback (optional) A callback to be called when
17569      * expanding this node completes (does not wait for deep expand to complete).
17570      * Called with 1 parameter, this node.
17571      */
17572     expand : function(deep, anim, callback){
17573         if(!this.expanded){
17574             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17575                 return;
17576             }
17577             if(!this.childrenRendered){
17578                 this.renderChildren();
17579             }
17580             this.expanded = true;
17581             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17582                 this.ui.animExpand(function(){
17583                     this.fireEvent("expand", this);
17584                     if(typeof callback == "function"){
17585                         callback(this);
17586                     }
17587                     if(deep === true){
17588                         this.expandChildNodes(true);
17589                     }
17590                 }.createDelegate(this));
17591                 return;
17592             }else{
17593                 this.ui.expand();
17594                 this.fireEvent("expand", this);
17595                 if(typeof callback == "function"){
17596                     callback(this);
17597                 }
17598             }
17599         }else{
17600            if(typeof callback == "function"){
17601                callback(this);
17602            }
17603         }
17604         if(deep === true){
17605             this.expandChildNodes(true);
17606         }
17607     },
17608
17609     isHiddenRoot : function(){
17610         return this.isRoot && !this.getOwnerTree().rootVisible;
17611     },
17612
17613     /**
17614      * Collapse this node.
17615      * @param {Boolean} deep (optional) True to collapse all children as well
17616      * @param {Boolean} anim (optional) false to cancel the default animation
17617      */
17618     collapse : function(deep, anim){
17619         if(this.expanded && !this.isHiddenRoot()){
17620             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17621                 return;
17622             }
17623             this.expanded = false;
17624             if((this.getOwnerTree().animate && anim !== false) || anim){
17625                 this.ui.animCollapse(function(){
17626                     this.fireEvent("collapse", this);
17627                     if(deep === true){
17628                         this.collapseChildNodes(true);
17629                     }
17630                 }.createDelegate(this));
17631                 return;
17632             }else{
17633                 this.ui.collapse();
17634                 this.fireEvent("collapse", this);
17635             }
17636         }
17637         if(deep === true){
17638             var cs = this.childNodes;
17639             for(var i = 0, len = cs.length; i < len; i++) {
17640                 cs[i].collapse(true, false);
17641             }
17642         }
17643     },
17644
17645     // private
17646     delayedExpand : function(delay){
17647         if(!this.expandProcId){
17648             this.expandProcId = this.expand.defer(delay, this);
17649         }
17650     },
17651
17652     // private
17653     cancelExpand : function(){
17654         if(this.expandProcId){
17655             clearTimeout(this.expandProcId);
17656         }
17657         this.expandProcId = false;
17658     },
17659
17660     /**
17661      * Toggles expanded/collapsed state of the node
17662      */
17663     toggle : function(){
17664         if(this.expanded){
17665             this.collapse();
17666         }else{
17667             this.expand();
17668         }
17669     },
17670
17671     /**
17672      * Ensures all parent nodes are expanded
17673      */
17674     ensureVisible : function(callback){
17675         var tree = this.getOwnerTree();
17676         tree.expandPath(this.parentNode.getPath(), false, function(){
17677             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17678             Roo.callback(callback);
17679         }.createDelegate(this));
17680     },
17681
17682     /**
17683      * Expand all child nodes
17684      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17685      */
17686     expandChildNodes : function(deep){
17687         var cs = this.childNodes;
17688         for(var i = 0, len = cs.length; i < len; i++) {
17689                 cs[i].expand(deep);
17690         }
17691     },
17692
17693     /**
17694      * Collapse all child nodes
17695      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17696      */
17697     collapseChildNodes : function(deep){
17698         var cs = this.childNodes;
17699         for(var i = 0, len = cs.length; i < len; i++) {
17700                 cs[i].collapse(deep);
17701         }
17702     },
17703
17704     /**
17705      * Disables this node
17706      */
17707     disable : function(){
17708         this.disabled = true;
17709         this.unselect();
17710         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17711             this.ui.onDisableChange(this, true);
17712         }
17713         this.fireEvent("disabledchange", this, true);
17714     },
17715
17716     /**
17717      * Enables this node
17718      */
17719     enable : function(){
17720         this.disabled = false;
17721         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17722             this.ui.onDisableChange(this, false);
17723         }
17724         this.fireEvent("disabledchange", this, false);
17725     },
17726
17727     // private
17728     renderChildren : function(suppressEvent){
17729         if(suppressEvent !== false){
17730             this.fireEvent("beforechildrenrendered", this);
17731         }
17732         var cs = this.childNodes;
17733         for(var i = 0, len = cs.length; i < len; i++){
17734             cs[i].render(true);
17735         }
17736         this.childrenRendered = true;
17737     },
17738
17739     // private
17740     sort : function(fn, scope){
17741         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17742         if(this.childrenRendered){
17743             var cs = this.childNodes;
17744             for(var i = 0, len = cs.length; i < len; i++){
17745                 cs[i].render(true);
17746             }
17747         }
17748     },
17749
17750     // private
17751     render : function(bulkRender){
17752         this.ui.render(bulkRender);
17753         if(!this.rendered){
17754             this.rendered = true;
17755             if(this.expanded){
17756                 this.expanded = false;
17757                 this.expand(false, false);
17758             }
17759         }
17760     },
17761
17762     // private
17763     renderIndent : function(deep, refresh){
17764         if(refresh){
17765             this.ui.childIndent = null;
17766         }
17767         this.ui.renderIndent();
17768         if(deep === true && this.childrenRendered){
17769             var cs = this.childNodes;
17770             for(var i = 0, len = cs.length; i < len; i++){
17771                 cs[i].renderIndent(true, refresh);
17772             }
17773         }
17774     }
17775 });/*
17776  * Based on:
17777  * Ext JS Library 1.1.1
17778  * Copyright(c) 2006-2007, Ext JS, LLC.
17779  *
17780  * Originally Released Under LGPL - original licence link has changed is not relivant.
17781  *
17782  * Fork - LGPL
17783  * <script type="text/javascript">
17784  */
17785  
17786 /**
17787  * @class Roo.tree.AsyncTreeNode
17788  * @extends Roo.tree.TreeNode
17789  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17790  * @constructor
17791  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17792  */
17793  Roo.tree.AsyncTreeNode = function(config){
17794     this.loaded = false;
17795     this.loading = false;
17796     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17797     /**
17798     * @event beforeload
17799     * Fires before this node is loaded, return false to cancel
17800     * @param {Node} this This node
17801     */
17802     this.addEvents({'beforeload':true, 'load': true});
17803     /**
17804     * @event load
17805     * Fires when this node is loaded
17806     * @param {Node} this This node
17807     */
17808     /**
17809      * The loader used by this node (defaults to using the tree's defined loader)
17810      * @type TreeLoader
17811      * @property loader
17812      */
17813 };
17814 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17815     expand : function(deep, anim, callback){
17816         if(this.loading){ // if an async load is already running, waiting til it's done
17817             var timer;
17818             var f = function(){
17819                 if(!this.loading){ // done loading
17820                     clearInterval(timer);
17821                     this.expand(deep, anim, callback);
17822                 }
17823             }.createDelegate(this);
17824             timer = setInterval(f, 200);
17825             return;
17826         }
17827         if(!this.loaded){
17828             if(this.fireEvent("beforeload", this) === false){
17829                 return;
17830             }
17831             this.loading = true;
17832             this.ui.beforeLoad(this);
17833             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17834             if(loader){
17835                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17836                 return;
17837             }
17838         }
17839         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17840     },
17841     
17842     /**
17843      * Returns true if this node is currently loading
17844      * @return {Boolean}
17845      */
17846     isLoading : function(){
17847         return this.loading;  
17848     },
17849     
17850     loadComplete : function(deep, anim, callback){
17851         this.loading = false;
17852         this.loaded = true;
17853         this.ui.afterLoad(this);
17854         this.fireEvent("load", this);
17855         this.expand(deep, anim, callback);
17856     },
17857     
17858     /**
17859      * Returns true if this node has been loaded
17860      * @return {Boolean}
17861      */
17862     isLoaded : function(){
17863         return this.loaded;
17864     },
17865     
17866     hasChildNodes : function(){
17867         if(!this.isLeaf() && !this.loaded){
17868             return true;
17869         }else{
17870             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17871         }
17872     },
17873
17874     /**
17875      * Trigger a reload for this node
17876      * @param {Function} callback
17877      */
17878     reload : function(callback){
17879         this.collapse(false, false);
17880         while(this.firstChild){
17881             this.removeChild(this.firstChild);
17882         }
17883         this.childrenRendered = false;
17884         this.loaded = false;
17885         if(this.isHiddenRoot()){
17886             this.expanded = false;
17887         }
17888         this.expand(false, false, callback);
17889     }
17890 });/*
17891  * Based on:
17892  * Ext JS Library 1.1.1
17893  * Copyright(c) 2006-2007, Ext JS, LLC.
17894  *
17895  * Originally Released Under LGPL - original licence link has changed is not relivant.
17896  *
17897  * Fork - LGPL
17898  * <script type="text/javascript">
17899  */
17900  
17901 /**
17902  * @class Roo.tree.TreeNodeUI
17903  * @constructor
17904  * @param {Object} node The node to render
17905  * The TreeNode UI implementation is separate from the
17906  * tree implementation. Unless you are customizing the tree UI,
17907  * you should never have to use this directly.
17908  */
17909 Roo.tree.TreeNodeUI = function(node){
17910     this.node = node;
17911     this.rendered = false;
17912     this.animating = false;
17913     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17914 };
17915
17916 Roo.tree.TreeNodeUI.prototype = {
17917     removeChild : function(node){
17918         if(this.rendered){
17919             this.ctNode.removeChild(node.ui.getEl());
17920         }
17921     },
17922
17923     beforeLoad : function(){
17924          this.addClass("x-tree-node-loading");
17925     },
17926
17927     afterLoad : function(){
17928          this.removeClass("x-tree-node-loading");
17929     },
17930
17931     onTextChange : function(node, text, oldText){
17932         if(this.rendered){
17933             this.textNode.innerHTML = text;
17934         }
17935     },
17936
17937     onDisableChange : function(node, state){
17938         this.disabled = state;
17939         if(state){
17940             this.addClass("x-tree-node-disabled");
17941         }else{
17942             this.removeClass("x-tree-node-disabled");
17943         }
17944     },
17945
17946     onSelectedChange : function(state){
17947         if(state){
17948             this.focus();
17949             this.addClass("x-tree-selected");
17950         }else{
17951             //this.blur();
17952             this.removeClass("x-tree-selected");
17953         }
17954     },
17955
17956     onMove : function(tree, node, oldParent, newParent, index, refNode){
17957         this.childIndent = null;
17958         if(this.rendered){
17959             var targetNode = newParent.ui.getContainer();
17960             if(!targetNode){//target not rendered
17961                 this.holder = document.createElement("div");
17962                 this.holder.appendChild(this.wrap);
17963                 return;
17964             }
17965             var insertBefore = refNode ? refNode.ui.getEl() : null;
17966             if(insertBefore){
17967                 targetNode.insertBefore(this.wrap, insertBefore);
17968             }else{
17969                 targetNode.appendChild(this.wrap);
17970             }
17971             this.node.renderIndent(true);
17972         }
17973     },
17974
17975     addClass : function(cls){
17976         if(this.elNode){
17977             Roo.fly(this.elNode).addClass(cls);
17978         }
17979     },
17980
17981     removeClass : function(cls){
17982         if(this.elNode){
17983             Roo.fly(this.elNode).removeClass(cls);
17984         }
17985     },
17986
17987     remove : function(){
17988         if(this.rendered){
17989             this.holder = document.createElement("div");
17990             this.holder.appendChild(this.wrap);
17991         }
17992     },
17993
17994     fireEvent : function(){
17995         return this.node.fireEvent.apply(this.node, arguments);
17996     },
17997
17998     initEvents : function(){
17999         this.node.on("move", this.onMove, this);
18000         var E = Roo.EventManager;
18001         var a = this.anchor;
18002
18003         var el = Roo.fly(a, '_treeui');
18004
18005         if(Roo.isOpera){ // opera render bug ignores the CSS
18006             el.setStyle("text-decoration", "none");
18007         }
18008
18009         el.on("click", this.onClick, this);
18010         el.on("dblclick", this.onDblClick, this);
18011
18012         if(this.checkbox){
18013             Roo.EventManager.on(this.checkbox,
18014                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18015         }
18016
18017         el.on("contextmenu", this.onContextMenu, this);
18018
18019         var icon = Roo.fly(this.iconNode);
18020         icon.on("click", this.onClick, this);
18021         icon.on("dblclick", this.onDblClick, this);
18022         icon.on("contextmenu", this.onContextMenu, this);
18023         E.on(this.ecNode, "click", this.ecClick, this, true);
18024
18025         if(this.node.disabled){
18026             this.addClass("x-tree-node-disabled");
18027         }
18028         if(this.node.hidden){
18029             this.addClass("x-tree-node-disabled");
18030         }
18031         var ot = this.node.getOwnerTree();
18032         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18033         if(dd && (!this.node.isRoot || ot.rootVisible)){
18034             Roo.dd.Registry.register(this.elNode, {
18035                 node: this.node,
18036                 handles: this.getDDHandles(),
18037                 isHandle: false
18038             });
18039         }
18040     },
18041
18042     getDDHandles : function(){
18043         return [this.iconNode, this.textNode];
18044     },
18045
18046     hide : function(){
18047         if(this.rendered){
18048             this.wrap.style.display = "none";
18049         }
18050     },
18051
18052     show : function(){
18053         if(this.rendered){
18054             this.wrap.style.display = "";
18055         }
18056     },
18057
18058     onContextMenu : function(e){
18059         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18060             e.preventDefault();
18061             this.focus();
18062             this.fireEvent("contextmenu", this.node, e);
18063         }
18064     },
18065
18066     onClick : function(e){
18067         if(this.dropping){
18068             e.stopEvent();
18069             return;
18070         }
18071         if(this.fireEvent("beforeclick", this.node, e) !== false){
18072             if(!this.disabled && this.node.attributes.href){
18073                 this.fireEvent("click", this.node, e);
18074                 return;
18075             }
18076             e.preventDefault();
18077             if(this.disabled){
18078                 return;
18079             }
18080
18081             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18082                 this.node.toggle();
18083             }
18084
18085             this.fireEvent("click", this.node, e);
18086         }else{
18087             e.stopEvent();
18088         }
18089     },
18090
18091     onDblClick : function(e){
18092         e.preventDefault();
18093         if(this.disabled){
18094             return;
18095         }
18096         if(this.checkbox){
18097             this.toggleCheck();
18098         }
18099         if(!this.animating && this.node.hasChildNodes()){
18100             this.node.toggle();
18101         }
18102         this.fireEvent("dblclick", this.node, e);
18103     },
18104
18105     onCheckChange : function(){
18106         var checked = this.checkbox.checked;
18107         this.node.attributes.checked = checked;
18108         this.fireEvent('checkchange', this.node, checked);
18109     },
18110
18111     ecClick : function(e){
18112         if(!this.animating && this.node.hasChildNodes()){
18113             this.node.toggle();
18114         }
18115     },
18116
18117     startDrop : function(){
18118         this.dropping = true;
18119     },
18120
18121     // delayed drop so the click event doesn't get fired on a drop
18122     endDrop : function(){
18123        setTimeout(function(){
18124            this.dropping = false;
18125        }.createDelegate(this), 50);
18126     },
18127
18128     expand : function(){
18129         this.updateExpandIcon();
18130         this.ctNode.style.display = "";
18131     },
18132
18133     focus : function(){
18134         if(!this.node.preventHScroll){
18135             try{this.anchor.focus();
18136             }catch(e){}
18137         }else if(!Roo.isIE){
18138             try{
18139                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18140                 var l = noscroll.scrollLeft;
18141                 this.anchor.focus();
18142                 noscroll.scrollLeft = l;
18143             }catch(e){}
18144         }
18145     },
18146
18147     toggleCheck : function(value){
18148         var cb = this.checkbox;
18149         if(cb){
18150             cb.checked = (value === undefined ? !cb.checked : value);
18151         }
18152     },
18153
18154     blur : function(){
18155         try{
18156             this.anchor.blur();
18157         }catch(e){}
18158     },
18159
18160     animExpand : function(callback){
18161         var ct = Roo.get(this.ctNode);
18162         ct.stopFx();
18163         if(!this.node.hasChildNodes()){
18164             this.updateExpandIcon();
18165             this.ctNode.style.display = "";
18166             Roo.callback(callback);
18167             return;
18168         }
18169         this.animating = true;
18170         this.updateExpandIcon();
18171
18172         ct.slideIn('t', {
18173            callback : function(){
18174                this.animating = false;
18175                Roo.callback(callback);
18176             },
18177             scope: this,
18178             duration: this.node.ownerTree.duration || .25
18179         });
18180     },
18181
18182     highlight : function(){
18183         var tree = this.node.getOwnerTree();
18184         Roo.fly(this.wrap).highlight(
18185             tree.hlColor || "C3DAF9",
18186             {endColor: tree.hlBaseColor}
18187         );
18188     },
18189
18190     collapse : function(){
18191         this.updateExpandIcon();
18192         this.ctNode.style.display = "none";
18193     },
18194
18195     animCollapse : function(callback){
18196         var ct = Roo.get(this.ctNode);
18197         ct.enableDisplayMode('block');
18198         ct.stopFx();
18199
18200         this.animating = true;
18201         this.updateExpandIcon();
18202
18203         ct.slideOut('t', {
18204             callback : function(){
18205                this.animating = false;
18206                Roo.callback(callback);
18207             },
18208             scope: this,
18209             duration: this.node.ownerTree.duration || .25
18210         });
18211     },
18212
18213     getContainer : function(){
18214         return this.ctNode;
18215     },
18216
18217     getEl : function(){
18218         return this.wrap;
18219     },
18220
18221     appendDDGhost : function(ghostNode){
18222         ghostNode.appendChild(this.elNode.cloneNode(true));
18223     },
18224
18225     getDDRepairXY : function(){
18226         return Roo.lib.Dom.getXY(this.iconNode);
18227     },
18228
18229     onRender : function(){
18230         this.render();
18231     },
18232
18233     render : function(bulkRender){
18234         var n = this.node, a = n.attributes;
18235         var targetNode = n.parentNode ?
18236               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18237
18238         if(!this.rendered){
18239             this.rendered = true;
18240
18241             this.renderElements(n, a, targetNode, bulkRender);
18242
18243             if(a.qtip){
18244                if(this.textNode.setAttributeNS){
18245                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18246                    if(a.qtipTitle){
18247                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18248                    }
18249                }else{
18250                    this.textNode.setAttribute("ext:qtip", a.qtip);
18251                    if(a.qtipTitle){
18252                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18253                    }
18254                }
18255             }else if(a.qtipCfg){
18256                 a.qtipCfg.target = Roo.id(this.textNode);
18257                 Roo.QuickTips.register(a.qtipCfg);
18258             }
18259             this.initEvents();
18260             if(!this.node.expanded){
18261                 this.updateExpandIcon();
18262             }
18263         }else{
18264             if(bulkRender === true) {
18265                 targetNode.appendChild(this.wrap);
18266             }
18267         }
18268     },
18269
18270     renderElements : function(n, a, targetNode, bulkRender)
18271     {
18272         // add some indent caching, this helps performance when rendering a large tree
18273         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18274         var t = n.getOwnerTree();
18275         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18276         if (typeof(n.attributes.html) != 'undefined') {
18277             txt = n.attributes.html;
18278         }
18279         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18280         var cb = typeof a.checked == 'boolean';
18281         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18282         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18283             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18284             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18285             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18286             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18287             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18288              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18289                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18290             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18291             "</li>"];
18292
18293         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18294             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18295                                 n.nextSibling.ui.getEl(), buf.join(""));
18296         }else{
18297             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18298         }
18299
18300         this.elNode = this.wrap.childNodes[0];
18301         this.ctNode = this.wrap.childNodes[1];
18302         var cs = this.elNode.childNodes;
18303         this.indentNode = cs[0];
18304         this.ecNode = cs[1];
18305         this.iconNode = cs[2];
18306         var index = 3;
18307         if(cb){
18308             this.checkbox = cs[3];
18309             index++;
18310         }
18311         this.anchor = cs[index];
18312         this.textNode = cs[index].firstChild;
18313     },
18314
18315     getAnchor : function(){
18316         return this.anchor;
18317     },
18318
18319     getTextEl : function(){
18320         return this.textNode;
18321     },
18322
18323     getIconEl : function(){
18324         return this.iconNode;
18325     },
18326
18327     isChecked : function(){
18328         return this.checkbox ? this.checkbox.checked : false;
18329     },
18330
18331     updateExpandIcon : function(){
18332         if(this.rendered){
18333             var n = this.node, c1, c2;
18334             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18335             var hasChild = n.hasChildNodes();
18336             if(hasChild){
18337                 if(n.expanded){
18338                     cls += "-minus";
18339                     c1 = "x-tree-node-collapsed";
18340                     c2 = "x-tree-node-expanded";
18341                 }else{
18342                     cls += "-plus";
18343                     c1 = "x-tree-node-expanded";
18344                     c2 = "x-tree-node-collapsed";
18345                 }
18346                 if(this.wasLeaf){
18347                     this.removeClass("x-tree-node-leaf");
18348                     this.wasLeaf = false;
18349                 }
18350                 if(this.c1 != c1 || this.c2 != c2){
18351                     Roo.fly(this.elNode).replaceClass(c1, c2);
18352                     this.c1 = c1; this.c2 = c2;
18353                 }
18354             }else{
18355                 // this changes non-leafs into leafs if they have no children.
18356                 // it's not very rational behaviour..
18357                 
18358                 if(!this.wasLeaf && this.node.leaf){
18359                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18360                     delete this.c1;
18361                     delete this.c2;
18362                     this.wasLeaf = true;
18363                 }
18364             }
18365             var ecc = "x-tree-ec-icon "+cls;
18366             if(this.ecc != ecc){
18367                 this.ecNode.className = ecc;
18368                 this.ecc = ecc;
18369             }
18370         }
18371     },
18372
18373     getChildIndent : function(){
18374         if(!this.childIndent){
18375             var buf = [];
18376             var p = this.node;
18377             while(p){
18378                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18379                     if(!p.isLast()) {
18380                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18381                     } else {
18382                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18383                     }
18384                 }
18385                 p = p.parentNode;
18386             }
18387             this.childIndent = buf.join("");
18388         }
18389         return this.childIndent;
18390     },
18391
18392     renderIndent : function(){
18393         if(this.rendered){
18394             var indent = "";
18395             var p = this.node.parentNode;
18396             if(p){
18397                 indent = p.ui.getChildIndent();
18398             }
18399             if(this.indentMarkup != indent){ // don't rerender if not required
18400                 this.indentNode.innerHTML = indent;
18401                 this.indentMarkup = indent;
18402             }
18403             this.updateExpandIcon();
18404         }
18405     }
18406 };
18407
18408 Roo.tree.RootTreeNodeUI = function(){
18409     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18410 };
18411 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18412     render : function(){
18413         if(!this.rendered){
18414             var targetNode = this.node.ownerTree.innerCt.dom;
18415             this.node.expanded = true;
18416             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18417             this.wrap = this.ctNode = targetNode.firstChild;
18418         }
18419     },
18420     collapse : function(){
18421     },
18422     expand : function(){
18423     }
18424 });/*
18425  * Based on:
18426  * Ext JS Library 1.1.1
18427  * Copyright(c) 2006-2007, Ext JS, LLC.
18428  *
18429  * Originally Released Under LGPL - original licence link has changed is not relivant.
18430  *
18431  * Fork - LGPL
18432  * <script type="text/javascript">
18433  */
18434 /**
18435  * @class Roo.tree.TreeLoader
18436  * @extends Roo.util.Observable
18437  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18438  * nodes from a specified URL. The response must be a javascript Array definition
18439  * who's elements are node definition objects. eg:
18440  * <pre><code>
18441 {  success : true,
18442    data :      [
18443    
18444     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18445     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18446     ]
18447 }
18448
18449
18450 </code></pre>
18451  * <br><br>
18452  * The old style respose with just an array is still supported, but not recommended.
18453  * <br><br>
18454  *
18455  * A server request is sent, and child nodes are loaded only when a node is expanded.
18456  * The loading node's id is passed to the server under the parameter name "node" to
18457  * enable the server to produce the correct child nodes.
18458  * <br><br>
18459  * To pass extra parameters, an event handler may be attached to the "beforeload"
18460  * event, and the parameters specified in the TreeLoader's baseParams property:
18461  * <pre><code>
18462     myTreeLoader.on("beforeload", function(treeLoader, node) {
18463         this.baseParams.category = node.attributes.category;
18464     }, this);
18465 </code></pre><
18466  * This would pass an HTTP parameter called "category" to the server containing
18467  * the value of the Node's "category" attribute.
18468  * @constructor
18469  * Creates a new Treeloader.
18470  * @param {Object} config A config object containing config properties.
18471  */
18472 Roo.tree.TreeLoader = function(config){
18473     this.baseParams = {};
18474     this.requestMethod = "POST";
18475     Roo.apply(this, config);
18476
18477     this.addEvents({
18478     
18479         /**
18480          * @event beforeload
18481          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18482          * @param {Object} This TreeLoader object.
18483          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18484          * @param {Object} callback The callback function specified in the {@link #load} call.
18485          */
18486         beforeload : true,
18487         /**
18488          * @event load
18489          * Fires when the node has been successfuly loaded.
18490          * @param {Object} This TreeLoader object.
18491          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18492          * @param {Object} response The response object containing the data from the server.
18493          */
18494         load : true,
18495         /**
18496          * @event loadexception
18497          * Fires if the network request failed.
18498          * @param {Object} This TreeLoader object.
18499          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18500          * @param {Object} response The response object containing the data from the server.
18501          */
18502         loadexception : true,
18503         /**
18504          * @event create
18505          * Fires before a node is created, enabling you to return custom Node types 
18506          * @param {Object} This TreeLoader object.
18507          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18508          */
18509         create : true
18510     });
18511
18512     Roo.tree.TreeLoader.superclass.constructor.call(this);
18513 };
18514
18515 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18516     /**
18517     * @cfg {String} dataUrl The URL from which to request a Json string which
18518     * specifies an array of node definition object representing the child nodes
18519     * to be loaded.
18520     */
18521     /**
18522     * @cfg {String} requestMethod either GET or POST
18523     * defaults to POST (due to BC)
18524     * to be loaded.
18525     */
18526     /**
18527     * @cfg {Object} baseParams (optional) An object containing properties which
18528     * specify HTTP parameters to be passed to each request for child nodes.
18529     */
18530     /**
18531     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18532     * created by this loader. If the attributes sent by the server have an attribute in this object,
18533     * they take priority.
18534     */
18535     /**
18536     * @cfg {Object} uiProviders (optional) An object containing properties which
18537     * 
18538     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18539     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18540     * <i>uiProvider</i> attribute of a returned child node is a string rather
18541     * than a reference to a TreeNodeUI implementation, this that string value
18542     * is used as a property name in the uiProviders object. You can define the provider named
18543     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18544     */
18545     uiProviders : {},
18546
18547     /**
18548     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18549     * child nodes before loading.
18550     */
18551     clearOnLoad : true,
18552
18553     /**
18554     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18555     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18556     * Grid query { data : [ .....] }
18557     */
18558     
18559     root : false,
18560      /**
18561     * @cfg {String} queryParam (optional) 
18562     * Name of the query as it will be passed on the querystring (defaults to 'node')
18563     * eg. the request will be ?node=[id]
18564     */
18565     
18566     
18567     queryParam: false,
18568     
18569     /**
18570      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18571      * This is called automatically when a node is expanded, but may be used to reload
18572      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18573      * @param {Roo.tree.TreeNode} node
18574      * @param {Function} callback
18575      */
18576     load : function(node, callback){
18577         if(this.clearOnLoad){
18578             while(node.firstChild){
18579                 node.removeChild(node.firstChild);
18580             }
18581         }
18582         if(node.attributes.children){ // preloaded json children
18583             var cs = node.attributes.children;
18584             for(var i = 0, len = cs.length; i < len; i++){
18585                 node.appendChild(this.createNode(cs[i]));
18586             }
18587             if(typeof callback == "function"){
18588                 callback();
18589             }
18590         }else if(this.dataUrl){
18591             this.requestData(node, callback);
18592         }
18593     },
18594
18595     getParams: function(node){
18596         var buf = [], bp = this.baseParams;
18597         for(var key in bp){
18598             if(typeof bp[key] != "function"){
18599                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18600             }
18601         }
18602         var n = this.queryParam === false ? 'node' : this.queryParam;
18603         buf.push(n + "=", encodeURIComponent(node.id));
18604         return buf.join("");
18605     },
18606
18607     requestData : function(node, callback){
18608         if(this.fireEvent("beforeload", this, node, callback) !== false){
18609             this.transId = Roo.Ajax.request({
18610                 method:this.requestMethod,
18611                 url: this.dataUrl||this.url,
18612                 success: this.handleResponse,
18613                 failure: this.handleFailure,
18614                 scope: this,
18615                 argument: {callback: callback, node: node},
18616                 params: this.getParams(node)
18617             });
18618         }else{
18619             // if the load is cancelled, make sure we notify
18620             // the node that we are done
18621             if(typeof callback == "function"){
18622                 callback();
18623             }
18624         }
18625     },
18626
18627     isLoading : function(){
18628         return this.transId ? true : false;
18629     },
18630
18631     abort : function(){
18632         if(this.isLoading()){
18633             Roo.Ajax.abort(this.transId);
18634         }
18635     },
18636
18637     // private
18638     createNode : function(attr)
18639     {
18640         // apply baseAttrs, nice idea Corey!
18641         if(this.baseAttrs){
18642             Roo.applyIf(attr, this.baseAttrs);
18643         }
18644         if(this.applyLoader !== false){
18645             attr.loader = this;
18646         }
18647         // uiProvider = depreciated..
18648         
18649         if(typeof(attr.uiProvider) == 'string'){
18650            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18651                 /**  eval:var:attr */ eval(attr.uiProvider);
18652         }
18653         if(typeof(this.uiProviders['default']) != 'undefined') {
18654             attr.uiProvider = this.uiProviders['default'];
18655         }
18656         
18657         this.fireEvent('create', this, attr);
18658         
18659         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18660         return(attr.leaf ?
18661                         new Roo.tree.TreeNode(attr) :
18662                         new Roo.tree.AsyncTreeNode(attr));
18663     },
18664
18665     processResponse : function(response, node, callback)
18666     {
18667         var json = response.responseText;
18668         try {
18669             
18670             var o = Roo.decode(json);
18671             
18672             if (this.root === false && typeof(o.success) != undefined) {
18673                 this.root = 'data'; // the default behaviour for list like data..
18674                 }
18675                 
18676             if (this.root !== false &&  !o.success) {
18677                 // it's a failure condition.
18678                 var a = response.argument;
18679                 this.fireEvent("loadexception", this, a.node, response);
18680                 Roo.log("Load failed - should have a handler really");
18681                 return;
18682             }
18683             
18684             
18685             
18686             if (this.root !== false) {
18687                  o = o[this.root];
18688             }
18689             
18690             for(var i = 0, len = o.length; i < len; i++){
18691                 var n = this.createNode(o[i]);
18692                 if(n){
18693                     node.appendChild(n);
18694                 }
18695             }
18696             if(typeof callback == "function"){
18697                 callback(this, node);
18698             }
18699         }catch(e){
18700             this.handleFailure(response);
18701         }
18702     },
18703
18704     handleResponse : function(response){
18705         this.transId = false;
18706         var a = response.argument;
18707         this.processResponse(response, a.node, a.callback);
18708         this.fireEvent("load", this, a.node, response);
18709     },
18710
18711     handleFailure : function(response)
18712     {
18713         // should handle failure better..
18714         this.transId = false;
18715         var a = response.argument;
18716         this.fireEvent("loadexception", this, a.node, response);
18717         if(typeof a.callback == "function"){
18718             a.callback(this, a.node);
18719         }
18720     }
18721 });/*
18722  * Based on:
18723  * Ext JS Library 1.1.1
18724  * Copyright(c) 2006-2007, Ext JS, LLC.
18725  *
18726  * Originally Released Under LGPL - original licence link has changed is not relivant.
18727  *
18728  * Fork - LGPL
18729  * <script type="text/javascript">
18730  */
18731
18732 /**
18733 * @class Roo.tree.TreeFilter
18734 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18735 * @param {TreePanel} tree
18736 * @param {Object} config (optional)
18737  */
18738 Roo.tree.TreeFilter = function(tree, config){
18739     this.tree = tree;
18740     this.filtered = {};
18741     Roo.apply(this, config);
18742 };
18743
18744 Roo.tree.TreeFilter.prototype = {
18745     clearBlank:false,
18746     reverse:false,
18747     autoClear:false,
18748     remove:false,
18749
18750      /**
18751      * Filter the data by a specific attribute.
18752      * @param {String/RegExp} value Either string that the attribute value
18753      * should start with or a RegExp to test against the attribute
18754      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18755      * @param {TreeNode} startNode (optional) The node to start the filter at.
18756      */
18757     filter : function(value, attr, startNode){
18758         attr = attr || "text";
18759         var f;
18760         if(typeof value == "string"){
18761             var vlen = value.length;
18762             // auto clear empty filter
18763             if(vlen == 0 && this.clearBlank){
18764                 this.clear();
18765                 return;
18766             }
18767             value = value.toLowerCase();
18768             f = function(n){
18769                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18770             };
18771         }else if(value.exec){ // regex?
18772             f = function(n){
18773                 return value.test(n.attributes[attr]);
18774             };
18775         }else{
18776             throw 'Illegal filter type, must be string or regex';
18777         }
18778         this.filterBy(f, null, startNode);
18779         },
18780
18781     /**
18782      * Filter by a function. The passed function will be called with each
18783      * node in the tree (or from the startNode). If the function returns true, the node is kept
18784      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18785      * @param {Function} fn The filter function
18786      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18787      */
18788     filterBy : function(fn, scope, startNode){
18789         startNode = startNode || this.tree.root;
18790         if(this.autoClear){
18791             this.clear();
18792         }
18793         var af = this.filtered, rv = this.reverse;
18794         var f = function(n){
18795             if(n == startNode){
18796                 return true;
18797             }
18798             if(af[n.id]){
18799                 return false;
18800             }
18801             var m = fn.call(scope || n, n);
18802             if(!m || rv){
18803                 af[n.id] = n;
18804                 n.ui.hide();
18805                 return false;
18806             }
18807             return true;
18808         };
18809         startNode.cascade(f);
18810         if(this.remove){
18811            for(var id in af){
18812                if(typeof id != "function"){
18813                    var n = af[id];
18814                    if(n && n.parentNode){
18815                        n.parentNode.removeChild(n);
18816                    }
18817                }
18818            }
18819         }
18820     },
18821
18822     /**
18823      * Clears the current filter. Note: with the "remove" option
18824      * set a filter cannot be cleared.
18825      */
18826     clear : function(){
18827         var t = this.tree;
18828         var af = this.filtered;
18829         for(var id in af){
18830             if(typeof id != "function"){
18831                 var n = af[id];
18832                 if(n){
18833                     n.ui.show();
18834                 }
18835             }
18836         }
18837         this.filtered = {};
18838     }
18839 };
18840 /*
18841  * Based on:
18842  * Ext JS Library 1.1.1
18843  * Copyright(c) 2006-2007, Ext JS, LLC.
18844  *
18845  * Originally Released Under LGPL - original licence link has changed is not relivant.
18846  *
18847  * Fork - LGPL
18848  * <script type="text/javascript">
18849  */
18850  
18851
18852 /**
18853  * @class Roo.tree.TreeSorter
18854  * Provides sorting of nodes in a TreePanel
18855  * 
18856  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18857  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18858  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18859  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18860  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18861  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18862  * @constructor
18863  * @param {TreePanel} tree
18864  * @param {Object} config
18865  */
18866 Roo.tree.TreeSorter = function(tree, config){
18867     Roo.apply(this, config);
18868     tree.on("beforechildrenrendered", this.doSort, this);
18869     tree.on("append", this.updateSort, this);
18870     tree.on("insert", this.updateSort, this);
18871     
18872     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18873     var p = this.property || "text";
18874     var sortType = this.sortType;
18875     var fs = this.folderSort;
18876     var cs = this.caseSensitive === true;
18877     var leafAttr = this.leafAttr || 'leaf';
18878
18879     this.sortFn = function(n1, n2){
18880         if(fs){
18881             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18882                 return 1;
18883             }
18884             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18885                 return -1;
18886             }
18887         }
18888         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18889         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18890         if(v1 < v2){
18891                         return dsc ? +1 : -1;
18892                 }else if(v1 > v2){
18893                         return dsc ? -1 : +1;
18894         }else{
18895                 return 0;
18896         }
18897     };
18898 };
18899
18900 Roo.tree.TreeSorter.prototype = {
18901     doSort : function(node){
18902         node.sort(this.sortFn);
18903     },
18904     
18905     compareNodes : function(n1, n2){
18906         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18907     },
18908     
18909     updateSort : function(tree, node){
18910         if(node.childrenRendered){
18911             this.doSort.defer(1, this, [node]);
18912         }
18913     }
18914 };/*
18915  * Based on:
18916  * Ext JS Library 1.1.1
18917  * Copyright(c) 2006-2007, Ext JS, LLC.
18918  *
18919  * Originally Released Under LGPL - original licence link has changed is not relivant.
18920  *
18921  * Fork - LGPL
18922  * <script type="text/javascript">
18923  */
18924
18925 if(Roo.dd.DropZone){
18926     
18927 Roo.tree.TreeDropZone = function(tree, config){
18928     this.allowParentInsert = false;
18929     this.allowContainerDrop = false;
18930     this.appendOnly = false;
18931     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18932     this.tree = tree;
18933     this.lastInsertClass = "x-tree-no-status";
18934     this.dragOverData = {};
18935 };
18936
18937 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18938     ddGroup : "TreeDD",
18939     
18940     expandDelay : 1000,
18941     
18942     expandNode : function(node){
18943         if(node.hasChildNodes() && !node.isExpanded()){
18944             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18945         }
18946     },
18947     
18948     queueExpand : function(node){
18949         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18950     },
18951     
18952     cancelExpand : function(){
18953         if(this.expandProcId){
18954             clearTimeout(this.expandProcId);
18955             this.expandProcId = false;
18956         }
18957     },
18958     
18959     isValidDropPoint : function(n, pt, dd, e, data){
18960         if(!n || !data){ return false; }
18961         var targetNode = n.node;
18962         var dropNode = data.node;
18963         // default drop rules
18964         if(!(targetNode && targetNode.isTarget && pt)){
18965             return false;
18966         }
18967         if(pt == "append" && targetNode.allowChildren === false){
18968             return false;
18969         }
18970         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18971             return false;
18972         }
18973         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18974             return false;
18975         }
18976         // reuse the object
18977         var overEvent = this.dragOverData;
18978         overEvent.tree = this.tree;
18979         overEvent.target = targetNode;
18980         overEvent.data = data;
18981         overEvent.point = pt;
18982         overEvent.source = dd;
18983         overEvent.rawEvent = e;
18984         overEvent.dropNode = dropNode;
18985         overEvent.cancel = false;  
18986         var result = this.tree.fireEvent("nodedragover", overEvent);
18987         return overEvent.cancel === false && result !== false;
18988     },
18989     
18990     getDropPoint : function(e, n, dd)
18991     {
18992         var tn = n.node;
18993         if(tn.isRoot){
18994             return tn.allowChildren !== false ? "append" : false; // always append for root
18995         }
18996         var dragEl = n.ddel;
18997         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18998         var y = Roo.lib.Event.getPageY(e);
18999         //var noAppend = tn.allowChildren === false || tn.isLeaf();
19000         
19001         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19002         var noAppend = tn.allowChildren === false;
19003         if(this.appendOnly || tn.parentNode.allowChildren === false){
19004             return noAppend ? false : "append";
19005         }
19006         var noBelow = false;
19007         if(!this.allowParentInsert){
19008             noBelow = tn.hasChildNodes() && tn.isExpanded();
19009         }
19010         var q = (b - t) / (noAppend ? 2 : 3);
19011         if(y >= t && y < (t + q)){
19012             return "above";
19013         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19014             return "below";
19015         }else{
19016             return "append";
19017         }
19018     },
19019     
19020     onNodeEnter : function(n, dd, e, data)
19021     {
19022         this.cancelExpand();
19023     },
19024     
19025     onNodeOver : function(n, dd, e, data)
19026     {
19027        
19028         var pt = this.getDropPoint(e, n, dd);
19029         var node = n.node;
19030         
19031         // auto node expand check
19032         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19033             this.queueExpand(node);
19034         }else if(pt != "append"){
19035             this.cancelExpand();
19036         }
19037         
19038         // set the insert point style on the target node
19039         var returnCls = this.dropNotAllowed;
19040         if(this.isValidDropPoint(n, pt, dd, e, data)){
19041            if(pt){
19042                var el = n.ddel;
19043                var cls;
19044                if(pt == "above"){
19045                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19046                    cls = "x-tree-drag-insert-above";
19047                }else if(pt == "below"){
19048                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19049                    cls = "x-tree-drag-insert-below";
19050                }else{
19051                    returnCls = "x-tree-drop-ok-append";
19052                    cls = "x-tree-drag-append";
19053                }
19054                if(this.lastInsertClass != cls){
19055                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19056                    this.lastInsertClass = cls;
19057                }
19058            }
19059        }
19060        return returnCls;
19061     },
19062     
19063     onNodeOut : function(n, dd, e, data){
19064         
19065         this.cancelExpand();
19066         this.removeDropIndicators(n);
19067     },
19068     
19069     onNodeDrop : function(n, dd, e, data){
19070         var point = this.getDropPoint(e, n, dd);
19071         var targetNode = n.node;
19072         targetNode.ui.startDrop();
19073         if(!this.isValidDropPoint(n, point, dd, e, data)){
19074             targetNode.ui.endDrop();
19075             return false;
19076         }
19077         // first try to find the drop node
19078         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19079         var dropEvent = {
19080             tree : this.tree,
19081             target: targetNode,
19082             data: data,
19083             point: point,
19084             source: dd,
19085             rawEvent: e,
19086             dropNode: dropNode,
19087             cancel: !dropNode   
19088         };
19089         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19090         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19091             targetNode.ui.endDrop();
19092             return false;
19093         }
19094         // allow target changing
19095         targetNode = dropEvent.target;
19096         if(point == "append" && !targetNode.isExpanded()){
19097             targetNode.expand(false, null, function(){
19098                 this.completeDrop(dropEvent);
19099             }.createDelegate(this));
19100         }else{
19101             this.completeDrop(dropEvent);
19102         }
19103         return true;
19104     },
19105     
19106     completeDrop : function(de){
19107         var ns = de.dropNode, p = de.point, t = de.target;
19108         if(!(ns instanceof Array)){
19109             ns = [ns];
19110         }
19111         var n;
19112         for(var i = 0, len = ns.length; i < len; i++){
19113             n = ns[i];
19114             if(p == "above"){
19115                 t.parentNode.insertBefore(n, t);
19116             }else if(p == "below"){
19117                 t.parentNode.insertBefore(n, t.nextSibling);
19118             }else{
19119                 t.appendChild(n);
19120             }
19121         }
19122         n.ui.focus();
19123         if(this.tree.hlDrop){
19124             n.ui.highlight();
19125         }
19126         t.ui.endDrop();
19127         this.tree.fireEvent("nodedrop", de);
19128     },
19129     
19130     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19131         if(this.tree.hlDrop){
19132             dropNode.ui.focus();
19133             dropNode.ui.highlight();
19134         }
19135         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19136     },
19137     
19138     getTree : function(){
19139         return this.tree;
19140     },
19141     
19142     removeDropIndicators : function(n){
19143         if(n && n.ddel){
19144             var el = n.ddel;
19145             Roo.fly(el).removeClass([
19146                     "x-tree-drag-insert-above",
19147                     "x-tree-drag-insert-below",
19148                     "x-tree-drag-append"]);
19149             this.lastInsertClass = "_noclass";
19150         }
19151     },
19152     
19153     beforeDragDrop : function(target, e, id){
19154         this.cancelExpand();
19155         return true;
19156     },
19157     
19158     afterRepair : function(data){
19159         if(data && Roo.enableFx){
19160             data.node.ui.highlight();
19161         }
19162         this.hideProxy();
19163     }
19164     
19165 });
19166
19167 }
19168 /*
19169  * Based on:
19170  * Ext JS Library 1.1.1
19171  * Copyright(c) 2006-2007, Ext JS, LLC.
19172  *
19173  * Originally Released Under LGPL - original licence link has changed is not relivant.
19174  *
19175  * Fork - LGPL
19176  * <script type="text/javascript">
19177  */
19178  
19179
19180 if(Roo.dd.DragZone){
19181 Roo.tree.TreeDragZone = function(tree, config){
19182     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19183     this.tree = tree;
19184 };
19185
19186 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19187     ddGroup : "TreeDD",
19188    
19189     onBeforeDrag : function(data, e){
19190         var n = data.node;
19191         return n && n.draggable && !n.disabled;
19192     },
19193      
19194     
19195     onInitDrag : function(e){
19196         var data = this.dragData;
19197         this.tree.getSelectionModel().select(data.node);
19198         this.proxy.update("");
19199         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19200         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19201     },
19202     
19203     getRepairXY : function(e, data){
19204         return data.node.ui.getDDRepairXY();
19205     },
19206     
19207     onEndDrag : function(data, e){
19208         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19209         if (this.scroller !== false) {
19210             Roo.log('clear scroller');
19211             window.clearInterval(this.scroller);
19212             this.scroller =false;
19213             
19214         }
19215         
19216     },
19217     
19218     onValidDrop : function(dd, e, id){
19219         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19220         this.hideProxy();
19221     },
19222     
19223     beforeInvalidDrop : function(e, id){
19224         // this scrolls the original position back into view
19225         var sm = this.tree.getSelectionModel();
19226         sm.clearSelections();
19227         sm.select(this.dragData.node);
19228     },
19229     autoScroll: function(x, y, h, w) {
19230         Roo.log("drop zone - autoscroll called");
19231         
19232         Roo.log(this.scroll ? "scroll=y": "scroll=m" );
19233         if (this.scroll) {
19234             // The client height
19235             var clientH = Roo.lib.Dom.getViewWidth();
19236
19237             // The client width
19238             var clientW = Roo.lib.Dom.getViewHeight();
19239
19240             // The amt scrolled down
19241             var st = this.DDM.getScrollTop();
19242
19243             // The amt scrolled right
19244             var sl = this.DDM.getScrollLeft();
19245
19246             // Location of the bottom of the element
19247             var bot = h + y;
19248
19249             // Location of the right of the element
19250             var right = w + x;
19251
19252             // The distance from the cursor to the bottom of the visible area,
19253             // adjusted so that we don't scroll if the cursor is beyond the
19254             // element drag constraints
19255             var toBot = (clientH + st - y - this.deltaY);
19256
19257             // The distance from the cursor to the right of the visible area
19258             var toRight = (clientW + sl - x - this.deltaX);
19259
19260
19261             // How close to the edge the cursor must be before we scroll
19262             // var thresh = (document.all) ? 100 : 40;
19263             var thresh = 40;
19264
19265             // How many pixels to scroll per autoscroll op.  This helps to reduce
19266             // clunky scrolling. IE is more sensitive about this ... it needs this
19267             // value to be higher.
19268             var scrAmt = (document.all) ? 80 : 30;
19269
19270             // Scroll down if we are near the bottom of the visible page and the
19271             // obj extends below the crease
19272             if ( bot > clientH && toBot < thresh ) {
19273                 window.scrollTo(sl, st + scrAmt);
19274             }
19275
19276             // Scroll up if the window is scrolled down and the top of the object
19277             // goes above the top border
19278             if ( y < st && st > 0 && y - st < thresh ) {
19279                 window.scrollTo(sl, st - scrAmt);
19280             }
19281
19282             // Scroll right if the obj is beyond the right border and the cursor is
19283             // near the border.
19284             if ( right > clientW && toRight < thresh ) {
19285                 window.scrollTo(sl + scrAmt, st);
19286             }
19287
19288             // Scroll left if the window has been scrolled to the right and the obj
19289             // extends past the left border
19290             if ( x < sl && sl > 0 && x - sl < thresh ) {
19291                 window.scrollTo(sl - scrAmt, st);
19292             }
19293         }
19294     }
19295 });
19296 }/*
19297  * Based on:
19298  * Ext JS Library 1.1.1
19299  * Copyright(c) 2006-2007, Ext JS, LLC.
19300  *
19301  * Originally Released Under LGPL - original licence link has changed is not relivant.
19302  *
19303  * Fork - LGPL
19304  * <script type="text/javascript">
19305  */
19306 /**
19307  * @class Roo.tree.TreeEditor
19308  * @extends Roo.Editor
19309  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19310  * as the editor field.
19311  * @constructor
19312  * @param {Object} config (used to be the tree panel.)
19313  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19314  * 
19315  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19316  * @cfg {Roo.form.TextField|Object} field The field configuration
19317  *
19318  * 
19319  */
19320 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19321     var tree = config;
19322     var field;
19323     if (oldconfig) { // old style..
19324         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19325     } else {
19326         // new style..
19327         tree = config.tree;
19328         config.field = config.field  || {};
19329         config.field.xtype = 'TextField';
19330         field = Roo.factory(config.field, Roo.form);
19331     }
19332     config = config || {};
19333     
19334     
19335     this.addEvents({
19336         /**
19337          * @event beforenodeedit
19338          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19339          * false from the handler of this event.
19340          * @param {Editor} this
19341          * @param {Roo.tree.Node} node 
19342          */
19343         "beforenodeedit" : true
19344     });
19345     
19346     //Roo.log(config);
19347     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19348
19349     this.tree = tree;
19350
19351     tree.on('beforeclick', this.beforeNodeClick, this);
19352     tree.getTreeEl().on('mousedown', this.hide, this);
19353     this.on('complete', this.updateNode, this);
19354     this.on('beforestartedit', this.fitToTree, this);
19355     this.on('startedit', this.bindScroll, this, {delay:10});
19356     this.on('specialkey', this.onSpecialKey, this);
19357 };
19358
19359 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19360     /**
19361      * @cfg {String} alignment
19362      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19363      */
19364     alignment: "l-l",
19365     // inherit
19366     autoSize: false,
19367     /**
19368      * @cfg {Boolean} hideEl
19369      * True to hide the bound element while the editor is displayed (defaults to false)
19370      */
19371     hideEl : false,
19372     /**
19373      * @cfg {String} cls
19374      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19375      */
19376     cls: "x-small-editor x-tree-editor",
19377     /**
19378      * @cfg {Boolean} shim
19379      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19380      */
19381     shim:false,
19382     // inherit
19383     shadow:"frame",
19384     /**
19385      * @cfg {Number} maxWidth
19386      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19387      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19388      * scroll and client offsets into account prior to each edit.
19389      */
19390     maxWidth: 250,
19391
19392     editDelay : 350,
19393
19394     // private
19395     fitToTree : function(ed, el){
19396         var td = this.tree.getTreeEl().dom, nd = el.dom;
19397         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19398             td.scrollLeft = nd.offsetLeft;
19399         }
19400         var w = Math.min(
19401                 this.maxWidth,
19402                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19403         this.setSize(w, '');
19404         
19405         return this.fireEvent('beforenodeedit', this, this.editNode);
19406         
19407     },
19408
19409     // private
19410     triggerEdit : function(node){
19411         this.completeEdit();
19412         this.editNode = node;
19413         this.startEdit(node.ui.textNode, node.text);
19414     },
19415
19416     // private
19417     bindScroll : function(){
19418         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19419     },
19420
19421     // private
19422     beforeNodeClick : function(node, e){
19423         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19424         this.lastClick = new Date();
19425         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19426             e.stopEvent();
19427             this.triggerEdit(node);
19428             return false;
19429         }
19430         return true;
19431     },
19432
19433     // private
19434     updateNode : function(ed, value){
19435         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19436         this.editNode.setText(value);
19437     },
19438
19439     // private
19440     onHide : function(){
19441         Roo.tree.TreeEditor.superclass.onHide.call(this);
19442         if(this.editNode){
19443             this.editNode.ui.focus();
19444         }
19445     },
19446
19447     // private
19448     onSpecialKey : function(field, e){
19449         var k = e.getKey();
19450         if(k == e.ESC){
19451             e.stopEvent();
19452             this.cancelEdit();
19453         }else if(k == e.ENTER && !e.hasModifier()){
19454             e.stopEvent();
19455             this.completeEdit();
19456         }
19457     }
19458 });//<Script type="text/javascript">
19459 /*
19460  * Based on:
19461  * Ext JS Library 1.1.1
19462  * Copyright(c) 2006-2007, Ext JS, LLC.
19463  *
19464  * Originally Released Under LGPL - original licence link has changed is not relivant.
19465  *
19466  * Fork - LGPL
19467  * <script type="text/javascript">
19468  */
19469  
19470 /**
19471  * Not documented??? - probably should be...
19472  */
19473
19474 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19475     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19476     
19477     renderElements : function(n, a, targetNode, bulkRender){
19478         //consel.log("renderElements?");
19479         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19480
19481         var t = n.getOwnerTree();
19482         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19483         
19484         var cols = t.columns;
19485         var bw = t.borderWidth;
19486         var c = cols[0];
19487         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19488          var cb = typeof a.checked == "boolean";
19489         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19490         var colcls = 'x-t-' + tid + '-c0';
19491         var buf = [
19492             '<li class="x-tree-node">',
19493             
19494                 
19495                 '<div class="x-tree-node-el ', a.cls,'">',
19496                     // extran...
19497                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19498                 
19499                 
19500                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19501                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19502                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19503                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19504                            (a.iconCls ? ' '+a.iconCls : ''),
19505                            '" unselectable="on" />',
19506                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19507                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19508                              
19509                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19510                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19511                             '<span unselectable="on" qtip="' + tx + '">',
19512                              tx,
19513                              '</span></a>' ,
19514                     '</div>',
19515                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19516                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19517                  ];
19518         for(var i = 1, len = cols.length; i < len; i++){
19519             c = cols[i];
19520             colcls = 'x-t-' + tid + '-c' +i;
19521             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19522             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19523                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19524                       "</div>");
19525          }
19526          
19527          buf.push(
19528             '</a>',
19529             '<div class="x-clear"></div></div>',
19530             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19531             "</li>");
19532         
19533         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19534             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19535                                 n.nextSibling.ui.getEl(), buf.join(""));
19536         }else{
19537             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19538         }
19539         var el = this.wrap.firstChild;
19540         this.elRow = el;
19541         this.elNode = el.firstChild;
19542         this.ranchor = el.childNodes[1];
19543         this.ctNode = this.wrap.childNodes[1];
19544         var cs = el.firstChild.childNodes;
19545         this.indentNode = cs[0];
19546         this.ecNode = cs[1];
19547         this.iconNode = cs[2];
19548         var index = 3;
19549         if(cb){
19550             this.checkbox = cs[3];
19551             index++;
19552         }
19553         this.anchor = cs[index];
19554         
19555         this.textNode = cs[index].firstChild;
19556         
19557         //el.on("click", this.onClick, this);
19558         //el.on("dblclick", this.onDblClick, this);
19559         
19560         
19561        // console.log(this);
19562     },
19563     initEvents : function(){
19564         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19565         
19566             
19567         var a = this.ranchor;
19568
19569         var el = Roo.get(a);
19570
19571         if(Roo.isOpera){ // opera render bug ignores the CSS
19572             el.setStyle("text-decoration", "none");
19573         }
19574
19575         el.on("click", this.onClick, this);
19576         el.on("dblclick", this.onDblClick, this);
19577         el.on("contextmenu", this.onContextMenu, this);
19578         
19579     },
19580     
19581     /*onSelectedChange : function(state){
19582         if(state){
19583             this.focus();
19584             this.addClass("x-tree-selected");
19585         }else{
19586             //this.blur();
19587             this.removeClass("x-tree-selected");
19588         }
19589     },*/
19590     addClass : function(cls){
19591         if(this.elRow){
19592             Roo.fly(this.elRow).addClass(cls);
19593         }
19594         
19595     },
19596     
19597     
19598     removeClass : function(cls){
19599         if(this.elRow){
19600             Roo.fly(this.elRow).removeClass(cls);
19601         }
19602     }
19603
19604     
19605     
19606 });//<Script type="text/javascript">
19607
19608 /*
19609  * Based on:
19610  * Ext JS Library 1.1.1
19611  * Copyright(c) 2006-2007, Ext JS, LLC.
19612  *
19613  * Originally Released Under LGPL - original licence link has changed is not relivant.
19614  *
19615  * Fork - LGPL
19616  * <script type="text/javascript">
19617  */
19618  
19619
19620 /**
19621  * @class Roo.tree.ColumnTree
19622  * @extends Roo.data.TreePanel
19623  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19624  * @cfg {int} borderWidth  compined right/left border allowance
19625  * @constructor
19626  * @param {String/HTMLElement/Element} el The container element
19627  * @param {Object} config
19628  */
19629 Roo.tree.ColumnTree =  function(el, config)
19630 {
19631    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19632    this.addEvents({
19633         /**
19634         * @event resize
19635         * Fire this event on a container when it resizes
19636         * @param {int} w Width
19637         * @param {int} h Height
19638         */
19639        "resize" : true
19640     });
19641     this.on('resize', this.onResize, this);
19642 };
19643
19644 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19645     //lines:false,
19646     
19647     
19648     borderWidth: Roo.isBorderBox ? 0 : 2, 
19649     headEls : false,
19650     
19651     render : function(){
19652         // add the header.....
19653        
19654         Roo.tree.ColumnTree.superclass.render.apply(this);
19655         
19656         this.el.addClass('x-column-tree');
19657         
19658         this.headers = this.el.createChild(
19659             {cls:'x-tree-headers'},this.innerCt.dom);
19660    
19661         var cols = this.columns, c;
19662         var totalWidth = 0;
19663         this.headEls = [];
19664         var  len = cols.length;
19665         for(var i = 0; i < len; i++){
19666              c = cols[i];
19667              totalWidth += c.width;
19668             this.headEls.push(this.headers.createChild({
19669                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19670                  cn: {
19671                      cls:'x-tree-hd-text',
19672                      html: c.header
19673                  },
19674                  style:'width:'+(c.width-this.borderWidth)+'px;'
19675              }));
19676         }
19677         this.headers.createChild({cls:'x-clear'});
19678         // prevent floats from wrapping when clipped
19679         this.headers.setWidth(totalWidth);
19680         //this.innerCt.setWidth(totalWidth);
19681         this.innerCt.setStyle({ overflow: 'auto' });
19682         this.onResize(this.width, this.height);
19683              
19684         
19685     },
19686     onResize : function(w,h)
19687     {
19688         this.height = h;
19689         this.width = w;
19690         // resize cols..
19691         this.innerCt.setWidth(this.width);
19692         this.innerCt.setHeight(this.height-20);
19693         
19694         // headers...
19695         var cols = this.columns, c;
19696         var totalWidth = 0;
19697         var expEl = false;
19698         var len = cols.length;
19699         for(var i = 0; i < len; i++){
19700             c = cols[i];
19701             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19702                 // it's the expander..
19703                 expEl  = this.headEls[i];
19704                 continue;
19705             }
19706             totalWidth += c.width;
19707             
19708         }
19709         if (expEl) {
19710             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19711         }
19712         this.headers.setWidth(w-20);
19713
19714         
19715         
19716         
19717     }
19718 });
19719 /*
19720  * Based on:
19721  * Ext JS Library 1.1.1
19722  * Copyright(c) 2006-2007, Ext JS, LLC.
19723  *
19724  * Originally Released Under LGPL - original licence link has changed is not relivant.
19725  *
19726  * Fork - LGPL
19727  * <script type="text/javascript">
19728  */
19729  
19730 /**
19731  * @class Roo.menu.Menu
19732  * @extends Roo.util.Observable
19733  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19734  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19735  * @constructor
19736  * Creates a new Menu
19737  * @param {Object} config Configuration options
19738  */
19739 Roo.menu.Menu = function(config){
19740     Roo.apply(this, config);
19741     this.id = this.id || Roo.id();
19742     this.addEvents({
19743         /**
19744          * @event beforeshow
19745          * Fires before this menu is displayed
19746          * @param {Roo.menu.Menu} this
19747          */
19748         beforeshow : true,
19749         /**
19750          * @event beforehide
19751          * Fires before this menu is hidden
19752          * @param {Roo.menu.Menu} this
19753          */
19754         beforehide : true,
19755         /**
19756          * @event show
19757          * Fires after this menu is displayed
19758          * @param {Roo.menu.Menu} this
19759          */
19760         show : true,
19761         /**
19762          * @event hide
19763          * Fires after this menu is hidden
19764          * @param {Roo.menu.Menu} this
19765          */
19766         hide : true,
19767         /**
19768          * @event click
19769          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19770          * @param {Roo.menu.Menu} this
19771          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19772          * @param {Roo.EventObject} e
19773          */
19774         click : true,
19775         /**
19776          * @event mouseover
19777          * Fires when the mouse is hovering over this menu
19778          * @param {Roo.menu.Menu} this
19779          * @param {Roo.EventObject} e
19780          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19781          */
19782         mouseover : true,
19783         /**
19784          * @event mouseout
19785          * Fires when the mouse exits this menu
19786          * @param {Roo.menu.Menu} this
19787          * @param {Roo.EventObject} e
19788          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19789          */
19790         mouseout : true,
19791         /**
19792          * @event itemclick
19793          * Fires when a menu item contained in this menu is clicked
19794          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19795          * @param {Roo.EventObject} e
19796          */
19797         itemclick: true
19798     });
19799     if (this.registerMenu) {
19800         Roo.menu.MenuMgr.register(this);
19801     }
19802     
19803     var mis = this.items;
19804     this.items = new Roo.util.MixedCollection();
19805     if(mis){
19806         this.add.apply(this, mis);
19807     }
19808 };
19809
19810 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19811     /**
19812      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19813      */
19814     minWidth : 120,
19815     /**
19816      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19817      * for bottom-right shadow (defaults to "sides")
19818      */
19819     shadow : "sides",
19820     /**
19821      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19822      * this menu (defaults to "tl-tr?")
19823      */
19824     subMenuAlign : "tl-tr?",
19825     /**
19826      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19827      * relative to its element of origin (defaults to "tl-bl?")
19828      */
19829     defaultAlign : "tl-bl?",
19830     /**
19831      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19832      */
19833     allowOtherMenus : false,
19834     /**
19835      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19836      */
19837     registerMenu : true,
19838
19839     hidden:true,
19840
19841     // private
19842     render : function(){
19843         if(this.el){
19844             return;
19845         }
19846         var el = this.el = new Roo.Layer({
19847             cls: "x-menu",
19848             shadow:this.shadow,
19849             constrain: false,
19850             parentEl: this.parentEl || document.body,
19851             zindex:15000
19852         });
19853
19854         this.keyNav = new Roo.menu.MenuNav(this);
19855
19856         if(this.plain){
19857             el.addClass("x-menu-plain");
19858         }
19859         if(this.cls){
19860             el.addClass(this.cls);
19861         }
19862         // generic focus element
19863         this.focusEl = el.createChild({
19864             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19865         });
19866         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19867         ul.on("click", this.onClick, this);
19868         ul.on("mouseover", this.onMouseOver, this);
19869         ul.on("mouseout", this.onMouseOut, this);
19870         this.items.each(function(item){
19871             var li = document.createElement("li");
19872             li.className = "x-menu-list-item";
19873             ul.dom.appendChild(li);
19874             item.render(li, this);
19875         }, this);
19876         this.ul = ul;
19877         this.autoWidth();
19878     },
19879
19880     // private
19881     autoWidth : function(){
19882         var el = this.el, ul = this.ul;
19883         if(!el){
19884             return;
19885         }
19886         var w = this.width;
19887         if(w){
19888             el.setWidth(w);
19889         }else if(Roo.isIE){
19890             el.setWidth(this.minWidth);
19891             var t = el.dom.offsetWidth; // force recalc
19892             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19893         }
19894     },
19895
19896     // private
19897     delayAutoWidth : function(){
19898         if(this.rendered){
19899             if(!this.awTask){
19900                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19901             }
19902             this.awTask.delay(20);
19903         }
19904     },
19905
19906     // private
19907     findTargetItem : function(e){
19908         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19909         if(t && t.menuItemId){
19910             return this.items.get(t.menuItemId);
19911         }
19912     },
19913
19914     // private
19915     onClick : function(e){
19916         var t;
19917         if(t = this.findTargetItem(e)){
19918             t.onClick(e);
19919             this.fireEvent("click", this, t, e);
19920         }
19921     },
19922
19923     // private
19924     setActiveItem : function(item, autoExpand){
19925         if(item != this.activeItem){
19926             if(this.activeItem){
19927                 this.activeItem.deactivate();
19928             }
19929             this.activeItem = item;
19930             item.activate(autoExpand);
19931         }else if(autoExpand){
19932             item.expandMenu();
19933         }
19934     },
19935
19936     // private
19937     tryActivate : function(start, step){
19938         var items = this.items;
19939         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19940             var item = items.get(i);
19941             if(!item.disabled && item.canActivate){
19942                 this.setActiveItem(item, false);
19943                 return item;
19944             }
19945         }
19946         return false;
19947     },
19948
19949     // private
19950     onMouseOver : function(e){
19951         var t;
19952         if(t = this.findTargetItem(e)){
19953             if(t.canActivate && !t.disabled){
19954                 this.setActiveItem(t, true);
19955             }
19956         }
19957         this.fireEvent("mouseover", this, e, t);
19958     },
19959
19960     // private
19961     onMouseOut : function(e){
19962         var t;
19963         if(t = this.findTargetItem(e)){
19964             if(t == this.activeItem && t.shouldDeactivate(e)){
19965                 this.activeItem.deactivate();
19966                 delete this.activeItem;
19967             }
19968         }
19969         this.fireEvent("mouseout", this, e, t);
19970     },
19971
19972     /**
19973      * Read-only.  Returns true if the menu is currently displayed, else false.
19974      * @type Boolean
19975      */
19976     isVisible : function(){
19977         return this.el && !this.hidden;
19978     },
19979
19980     /**
19981      * Displays this menu relative to another element
19982      * @param {String/HTMLElement/Roo.Element} element The element to align to
19983      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19984      * the element (defaults to this.defaultAlign)
19985      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19986      */
19987     show : function(el, pos, parentMenu){
19988         this.parentMenu = parentMenu;
19989         if(!this.el){
19990             this.render();
19991         }
19992         this.fireEvent("beforeshow", this);
19993         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19994     },
19995
19996     /**
19997      * Displays this menu at a specific xy position
19998      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19999      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20000      */
20001     showAt : function(xy, parentMenu, /* private: */_e){
20002         this.parentMenu = parentMenu;
20003         if(!this.el){
20004             this.render();
20005         }
20006         if(_e !== false){
20007             this.fireEvent("beforeshow", this);
20008             xy = this.el.adjustForConstraints(xy);
20009         }
20010         this.el.setXY(xy);
20011         this.el.show();
20012         this.hidden = false;
20013         this.focus();
20014         this.fireEvent("show", this);
20015     },
20016
20017     focus : function(){
20018         if(!this.hidden){
20019             this.doFocus.defer(50, this);
20020         }
20021     },
20022
20023     doFocus : function(){
20024         if(!this.hidden){
20025             this.focusEl.focus();
20026         }
20027     },
20028
20029     /**
20030      * Hides this menu and optionally all parent menus
20031      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
20032      */
20033     hide : function(deep){
20034         if(this.el && this.isVisible()){
20035             this.fireEvent("beforehide", this);
20036             if(this.activeItem){
20037                 this.activeItem.deactivate();
20038                 this.activeItem = null;
20039             }
20040             this.el.hide();
20041             this.hidden = true;
20042             this.fireEvent("hide", this);
20043         }
20044         if(deep === true && this.parentMenu){
20045             this.parentMenu.hide(true);
20046         }
20047     },
20048
20049     /**
20050      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
20051      * Any of the following are valid:
20052      * <ul>
20053      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
20054      * <li>An HTMLElement object which will be converted to a menu item</li>
20055      * <li>A menu item config object that will be created as a new menu item</li>
20056      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
20057      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
20058      * </ul>
20059      * Usage:
20060      * <pre><code>
20061 // Create the menu
20062 var menu = new Roo.menu.Menu();
20063
20064 // Create a menu item to add by reference
20065 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20066
20067 // Add a bunch of items at once using different methods.
20068 // Only the last item added will be returned.
20069 var item = menu.add(
20070     menuItem,                // add existing item by ref
20071     'Dynamic Item',          // new TextItem
20072     '-',                     // new separator
20073     { text: 'Config Item' }  // new item by config
20074 );
20075 </code></pre>
20076      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20077      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20078      */
20079     add : function(){
20080         var a = arguments, l = a.length, item;
20081         for(var i = 0; i < l; i++){
20082             var el = a[i];
20083             if ((typeof(el) == "object") && el.xtype && el.xns) {
20084                 el = Roo.factory(el, Roo.menu);
20085             }
20086             
20087             if(el.render){ // some kind of Item
20088                 item = this.addItem(el);
20089             }else if(typeof el == "string"){ // string
20090                 if(el == "separator" || el == "-"){
20091                     item = this.addSeparator();
20092                 }else{
20093                     item = this.addText(el);
20094                 }
20095             }else if(el.tagName || el.el){ // element
20096                 item = this.addElement(el);
20097             }else if(typeof el == "object"){ // must be menu item config?
20098                 item = this.addMenuItem(el);
20099             }
20100         }
20101         return item;
20102     },
20103
20104     /**
20105      * Returns this menu's underlying {@link Roo.Element} object
20106      * @return {Roo.Element} The element
20107      */
20108     getEl : function(){
20109         if(!this.el){
20110             this.render();
20111         }
20112         return this.el;
20113     },
20114
20115     /**
20116      * Adds a separator bar to the menu
20117      * @return {Roo.menu.Item} The menu item that was added
20118      */
20119     addSeparator : function(){
20120         return this.addItem(new Roo.menu.Separator());
20121     },
20122
20123     /**
20124      * Adds an {@link Roo.Element} object to the menu
20125      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20126      * @return {Roo.menu.Item} The menu item that was added
20127      */
20128     addElement : function(el){
20129         return this.addItem(new Roo.menu.BaseItem(el));
20130     },
20131
20132     /**
20133      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20134      * @param {Roo.menu.Item} item The menu item to add
20135      * @return {Roo.menu.Item} The menu item that was added
20136      */
20137     addItem : function(item){
20138         this.items.add(item);
20139         if(this.ul){
20140             var li = document.createElement("li");
20141             li.className = "x-menu-list-item";
20142             this.ul.dom.appendChild(li);
20143             item.render(li, this);
20144             this.delayAutoWidth();
20145         }
20146         return item;
20147     },
20148
20149     /**
20150      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20151      * @param {Object} config A MenuItem config object
20152      * @return {Roo.menu.Item} The menu item that was added
20153      */
20154     addMenuItem : function(config){
20155         if(!(config instanceof Roo.menu.Item)){
20156             if(typeof config.checked == "boolean"){ // must be check menu item config?
20157                 config = new Roo.menu.CheckItem(config);
20158             }else{
20159                 config = new Roo.menu.Item(config);
20160             }
20161         }
20162         return this.addItem(config);
20163     },
20164
20165     /**
20166      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20167      * @param {String} text The text to display in the menu item
20168      * @return {Roo.menu.Item} The menu item that was added
20169      */
20170     addText : function(text){
20171         return this.addItem(new Roo.menu.TextItem({ text : text }));
20172     },
20173
20174     /**
20175      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20176      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20177      * @param {Roo.menu.Item} item The menu item to add
20178      * @return {Roo.menu.Item} The menu item that was added
20179      */
20180     insert : function(index, item){
20181         this.items.insert(index, item);
20182         if(this.ul){
20183             var li = document.createElement("li");
20184             li.className = "x-menu-list-item";
20185             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20186             item.render(li, this);
20187             this.delayAutoWidth();
20188         }
20189         return item;
20190     },
20191
20192     /**
20193      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20194      * @param {Roo.menu.Item} item The menu item to remove
20195      */
20196     remove : function(item){
20197         this.items.removeKey(item.id);
20198         item.destroy();
20199     },
20200
20201     /**
20202      * Removes and destroys all items in the menu
20203      */
20204     removeAll : function(){
20205         var f;
20206         while(f = this.items.first()){
20207             this.remove(f);
20208         }
20209     }
20210 });
20211
20212 // MenuNav is a private utility class used internally by the Menu
20213 Roo.menu.MenuNav = function(menu){
20214     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20215     this.scope = this.menu = menu;
20216 };
20217
20218 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20219     doRelay : function(e, h){
20220         var k = e.getKey();
20221         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20222             this.menu.tryActivate(0, 1);
20223             return false;
20224         }
20225         return h.call(this.scope || this, e, this.menu);
20226     },
20227
20228     up : function(e, m){
20229         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20230             m.tryActivate(m.items.length-1, -1);
20231         }
20232     },
20233
20234     down : function(e, m){
20235         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20236             m.tryActivate(0, 1);
20237         }
20238     },
20239
20240     right : function(e, m){
20241         if(m.activeItem){
20242             m.activeItem.expandMenu(true);
20243         }
20244     },
20245
20246     left : function(e, m){
20247         m.hide();
20248         if(m.parentMenu && m.parentMenu.activeItem){
20249             m.parentMenu.activeItem.activate();
20250         }
20251     },
20252
20253     enter : function(e, m){
20254         if(m.activeItem){
20255             e.stopPropagation();
20256             m.activeItem.onClick(e);
20257             m.fireEvent("click", this, m.activeItem);
20258             return true;
20259         }
20260     }
20261 });/*
20262  * Based on:
20263  * Ext JS Library 1.1.1
20264  * Copyright(c) 2006-2007, Ext JS, LLC.
20265  *
20266  * Originally Released Under LGPL - original licence link has changed is not relivant.
20267  *
20268  * Fork - LGPL
20269  * <script type="text/javascript">
20270  */
20271  
20272 /**
20273  * @class Roo.menu.MenuMgr
20274  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20275  * @singleton
20276  */
20277 Roo.menu.MenuMgr = function(){
20278    var menus, active, groups = {}, attached = false, lastShow = new Date();
20279
20280    // private - called when first menu is created
20281    function init(){
20282        menus = {};
20283        active = new Roo.util.MixedCollection();
20284        Roo.get(document).addKeyListener(27, function(){
20285            if(active.length > 0){
20286                hideAll();
20287            }
20288        });
20289    }
20290
20291    // private
20292    function hideAll(){
20293        if(active && active.length > 0){
20294            var c = active.clone();
20295            c.each(function(m){
20296                m.hide();
20297            });
20298        }
20299    }
20300
20301    // private
20302    function onHide(m){
20303        active.remove(m);
20304        if(active.length < 1){
20305            Roo.get(document).un("mousedown", onMouseDown);
20306            attached = false;
20307        }
20308    }
20309
20310    // private
20311    function onShow(m){
20312        var last = active.last();
20313        lastShow = new Date();
20314        active.add(m);
20315        if(!attached){
20316            Roo.get(document).on("mousedown", onMouseDown);
20317            attached = true;
20318        }
20319        if(m.parentMenu){
20320           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20321           m.parentMenu.activeChild = m;
20322        }else if(last && last.isVisible()){
20323           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20324        }
20325    }
20326
20327    // private
20328    function onBeforeHide(m){
20329        if(m.activeChild){
20330            m.activeChild.hide();
20331        }
20332        if(m.autoHideTimer){
20333            clearTimeout(m.autoHideTimer);
20334            delete m.autoHideTimer;
20335        }
20336    }
20337
20338    // private
20339    function onBeforeShow(m){
20340        var pm = m.parentMenu;
20341        if(!pm && !m.allowOtherMenus){
20342            hideAll();
20343        }else if(pm && pm.activeChild && active != m){
20344            pm.activeChild.hide();
20345        }
20346    }
20347
20348    // private
20349    function onMouseDown(e){
20350        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20351            hideAll();
20352        }
20353    }
20354
20355    // private
20356    function onBeforeCheck(mi, state){
20357        if(state){
20358            var g = groups[mi.group];
20359            for(var i = 0, l = g.length; i < l; i++){
20360                if(g[i] != mi){
20361                    g[i].setChecked(false);
20362                }
20363            }
20364        }
20365    }
20366
20367    return {
20368
20369        /**
20370         * Hides all menus that are currently visible
20371         */
20372        hideAll : function(){
20373             hideAll();  
20374        },
20375
20376        // private
20377        register : function(menu){
20378            if(!menus){
20379                init();
20380            }
20381            menus[menu.id] = menu;
20382            menu.on("beforehide", onBeforeHide);
20383            menu.on("hide", onHide);
20384            menu.on("beforeshow", onBeforeShow);
20385            menu.on("show", onShow);
20386            var g = menu.group;
20387            if(g && menu.events["checkchange"]){
20388                if(!groups[g]){
20389                    groups[g] = [];
20390                }
20391                groups[g].push(menu);
20392                menu.on("checkchange", onCheck);
20393            }
20394        },
20395
20396         /**
20397          * Returns a {@link Roo.menu.Menu} object
20398          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20399          * be used to generate and return a new Menu instance.
20400          */
20401        get : function(menu){
20402            if(typeof menu == "string"){ // menu id
20403                return menus[menu];
20404            }else if(menu.events){  // menu instance
20405                return menu;
20406            }else if(typeof menu.length == 'number'){ // array of menu items?
20407                return new Roo.menu.Menu({items:menu});
20408            }else{ // otherwise, must be a config
20409                return new Roo.menu.Menu(menu);
20410            }
20411        },
20412
20413        // private
20414        unregister : function(menu){
20415            delete menus[menu.id];
20416            menu.un("beforehide", onBeforeHide);
20417            menu.un("hide", onHide);
20418            menu.un("beforeshow", onBeforeShow);
20419            menu.un("show", onShow);
20420            var g = menu.group;
20421            if(g && menu.events["checkchange"]){
20422                groups[g].remove(menu);
20423                menu.un("checkchange", onCheck);
20424            }
20425        },
20426
20427        // private
20428        registerCheckable : function(menuItem){
20429            var g = menuItem.group;
20430            if(g){
20431                if(!groups[g]){
20432                    groups[g] = [];
20433                }
20434                groups[g].push(menuItem);
20435                menuItem.on("beforecheckchange", onBeforeCheck);
20436            }
20437        },
20438
20439        // private
20440        unregisterCheckable : function(menuItem){
20441            var g = menuItem.group;
20442            if(g){
20443                groups[g].remove(menuItem);
20444                menuItem.un("beforecheckchange", onBeforeCheck);
20445            }
20446        }
20447    };
20448 }();/*
20449  * Based on:
20450  * Ext JS Library 1.1.1
20451  * Copyright(c) 2006-2007, Ext JS, LLC.
20452  *
20453  * Originally Released Under LGPL - original licence link has changed is not relivant.
20454  *
20455  * Fork - LGPL
20456  * <script type="text/javascript">
20457  */
20458  
20459
20460 /**
20461  * @class Roo.menu.BaseItem
20462  * @extends Roo.Component
20463  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20464  * management and base configuration options shared by all menu components.
20465  * @constructor
20466  * Creates a new BaseItem
20467  * @param {Object} config Configuration options
20468  */
20469 Roo.menu.BaseItem = function(config){
20470     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20471
20472     this.addEvents({
20473         /**
20474          * @event click
20475          * Fires when this item is clicked
20476          * @param {Roo.menu.BaseItem} this
20477          * @param {Roo.EventObject} e
20478          */
20479         click: true,
20480         /**
20481          * @event activate
20482          * Fires when this item is activated
20483          * @param {Roo.menu.BaseItem} this
20484          */
20485         activate : true,
20486         /**
20487          * @event deactivate
20488          * Fires when this item is deactivated
20489          * @param {Roo.menu.BaseItem} this
20490          */
20491         deactivate : true
20492     });
20493
20494     if(this.handler){
20495         this.on("click", this.handler, this.scope, true);
20496     }
20497 };
20498
20499 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20500     /**
20501      * @cfg {Function} handler
20502      * A function that will handle the click event of this menu item (defaults to undefined)
20503      */
20504     /**
20505      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20506      */
20507     canActivate : false,
20508     /**
20509      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20510      */
20511     activeClass : "x-menu-item-active",
20512     /**
20513      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20514      */
20515     hideOnClick : true,
20516     /**
20517      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20518      */
20519     hideDelay : 100,
20520
20521     // private
20522     ctype: "Roo.menu.BaseItem",
20523
20524     // private
20525     actionMode : "container",
20526
20527     // private
20528     render : function(container, parentMenu){
20529         this.parentMenu = parentMenu;
20530         Roo.menu.BaseItem.superclass.render.call(this, container);
20531         this.container.menuItemId = this.id;
20532     },
20533
20534     // private
20535     onRender : function(container, position){
20536         this.el = Roo.get(this.el);
20537         container.dom.appendChild(this.el.dom);
20538     },
20539
20540     // private
20541     onClick : function(e){
20542         if(!this.disabled && this.fireEvent("click", this, e) !== false
20543                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20544             this.handleClick(e);
20545         }else{
20546             e.stopEvent();
20547         }
20548     },
20549
20550     // private
20551     activate : function(){
20552         if(this.disabled){
20553             return false;
20554         }
20555         var li = this.container;
20556         li.addClass(this.activeClass);
20557         this.region = li.getRegion().adjust(2, 2, -2, -2);
20558         this.fireEvent("activate", this);
20559         return true;
20560     },
20561
20562     // private
20563     deactivate : function(){
20564         this.container.removeClass(this.activeClass);
20565         this.fireEvent("deactivate", this);
20566     },
20567
20568     // private
20569     shouldDeactivate : function(e){
20570         return !this.region || !this.region.contains(e.getPoint());
20571     },
20572
20573     // private
20574     handleClick : function(e){
20575         if(this.hideOnClick){
20576             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20577         }
20578     },
20579
20580     // private
20581     expandMenu : function(autoActivate){
20582         // do nothing
20583     },
20584
20585     // private
20586     hideMenu : function(){
20587         // do nothing
20588     }
20589 });/*
20590  * Based on:
20591  * Ext JS Library 1.1.1
20592  * Copyright(c) 2006-2007, Ext JS, LLC.
20593  *
20594  * Originally Released Under LGPL - original licence link has changed is not relivant.
20595  *
20596  * Fork - LGPL
20597  * <script type="text/javascript">
20598  */
20599  
20600 /**
20601  * @class Roo.menu.Adapter
20602  * @extends Roo.menu.BaseItem
20603  * 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.
20604  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20605  * @constructor
20606  * Creates a new Adapter
20607  * @param {Object} config Configuration options
20608  */
20609 Roo.menu.Adapter = function(component, config){
20610     Roo.menu.Adapter.superclass.constructor.call(this, config);
20611     this.component = component;
20612 };
20613 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20614     // private
20615     canActivate : true,
20616
20617     // private
20618     onRender : function(container, position){
20619         this.component.render(container);
20620         this.el = this.component.getEl();
20621     },
20622
20623     // private
20624     activate : function(){
20625         if(this.disabled){
20626             return false;
20627         }
20628         this.component.focus();
20629         this.fireEvent("activate", this);
20630         return true;
20631     },
20632
20633     // private
20634     deactivate : function(){
20635         this.fireEvent("deactivate", this);
20636     },
20637
20638     // private
20639     disable : function(){
20640         this.component.disable();
20641         Roo.menu.Adapter.superclass.disable.call(this);
20642     },
20643
20644     // private
20645     enable : function(){
20646         this.component.enable();
20647         Roo.menu.Adapter.superclass.enable.call(this);
20648     }
20649 });/*
20650  * Based on:
20651  * Ext JS Library 1.1.1
20652  * Copyright(c) 2006-2007, Ext JS, LLC.
20653  *
20654  * Originally Released Under LGPL - original licence link has changed is not relivant.
20655  *
20656  * Fork - LGPL
20657  * <script type="text/javascript">
20658  */
20659
20660 /**
20661  * @class Roo.menu.TextItem
20662  * @extends Roo.menu.BaseItem
20663  * Adds a static text string to a menu, usually used as either a heading or group separator.
20664  * Note: old style constructor with text is still supported.
20665  * 
20666  * @constructor
20667  * Creates a new TextItem
20668  * @param {Object} cfg Configuration
20669  */
20670 Roo.menu.TextItem = function(cfg){
20671     if (typeof(cfg) == 'string') {
20672         this.text = cfg;
20673     } else {
20674         Roo.apply(this,cfg);
20675     }
20676     
20677     Roo.menu.TextItem.superclass.constructor.call(this);
20678 };
20679
20680 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20681     /**
20682      * @cfg {Boolean} text Text to show on item.
20683      */
20684     text : '',
20685     
20686     /**
20687      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20688      */
20689     hideOnClick : false,
20690     /**
20691      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20692      */
20693     itemCls : "x-menu-text",
20694
20695     // private
20696     onRender : function(){
20697         var s = document.createElement("span");
20698         s.className = this.itemCls;
20699         s.innerHTML = this.text;
20700         this.el = s;
20701         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20702     }
20703 });/*
20704  * Based on:
20705  * Ext JS Library 1.1.1
20706  * Copyright(c) 2006-2007, Ext JS, LLC.
20707  *
20708  * Originally Released Under LGPL - original licence link has changed is not relivant.
20709  *
20710  * Fork - LGPL
20711  * <script type="text/javascript">
20712  */
20713
20714 /**
20715  * @class Roo.menu.Separator
20716  * @extends Roo.menu.BaseItem
20717  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20718  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20719  * @constructor
20720  * @param {Object} config Configuration options
20721  */
20722 Roo.menu.Separator = function(config){
20723     Roo.menu.Separator.superclass.constructor.call(this, config);
20724 };
20725
20726 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20727     /**
20728      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20729      */
20730     itemCls : "x-menu-sep",
20731     /**
20732      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20733      */
20734     hideOnClick : false,
20735
20736     // private
20737     onRender : function(li){
20738         var s = document.createElement("span");
20739         s.className = this.itemCls;
20740         s.innerHTML = "&#160;";
20741         this.el = s;
20742         li.addClass("x-menu-sep-li");
20743         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20744     }
20745 });/*
20746  * Based on:
20747  * Ext JS Library 1.1.1
20748  * Copyright(c) 2006-2007, Ext JS, LLC.
20749  *
20750  * Originally Released Under LGPL - original licence link has changed is not relivant.
20751  *
20752  * Fork - LGPL
20753  * <script type="text/javascript">
20754  */
20755 /**
20756  * @class Roo.menu.Item
20757  * @extends Roo.menu.BaseItem
20758  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20759  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20760  * activation and click handling.
20761  * @constructor
20762  * Creates a new Item
20763  * @param {Object} config Configuration options
20764  */
20765 Roo.menu.Item = function(config){
20766     Roo.menu.Item.superclass.constructor.call(this, config);
20767     if(this.menu){
20768         this.menu = Roo.menu.MenuMgr.get(this.menu);
20769     }
20770 };
20771 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20772     
20773     /**
20774      * @cfg {String} text
20775      * The text to show on the menu item.
20776      */
20777     text: '',
20778      /**
20779      * @cfg {String} HTML to render in menu
20780      * The text to show on the menu item (HTML version).
20781      */
20782     html: '',
20783     /**
20784      * @cfg {String} icon
20785      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20786      */
20787     icon: undefined,
20788     /**
20789      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20790      */
20791     itemCls : "x-menu-item",
20792     /**
20793      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20794      */
20795     canActivate : true,
20796     /**
20797      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20798      */
20799     showDelay: 200,
20800     // doc'd in BaseItem
20801     hideDelay: 200,
20802
20803     // private
20804     ctype: "Roo.menu.Item",
20805     
20806     // private
20807     onRender : function(container, position){
20808         var el = document.createElement("a");
20809         el.hideFocus = true;
20810         el.unselectable = "on";
20811         el.href = this.href || "#";
20812         if(this.hrefTarget){
20813             el.target = this.hrefTarget;
20814         }
20815         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20816         
20817         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20818         
20819         el.innerHTML = String.format(
20820                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20821                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20822         this.el = el;
20823         Roo.menu.Item.superclass.onRender.call(this, container, position);
20824     },
20825
20826     /**
20827      * Sets the text to display in this menu item
20828      * @param {String} text The text to display
20829      * @param {Boolean} isHTML true to indicate text is pure html.
20830      */
20831     setText : function(text, isHTML){
20832         if (isHTML) {
20833             this.html = text;
20834         } else {
20835             this.text = text;
20836             this.html = '';
20837         }
20838         if(this.rendered){
20839             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20840      
20841             this.el.update(String.format(
20842                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20843                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20844             this.parentMenu.autoWidth();
20845         }
20846     },
20847
20848     // private
20849     handleClick : function(e){
20850         if(!this.href){ // if no link defined, stop the event automatically
20851             e.stopEvent();
20852         }
20853         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20854     },
20855
20856     // private
20857     activate : function(autoExpand){
20858         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20859             this.focus();
20860             if(autoExpand){
20861                 this.expandMenu();
20862             }
20863         }
20864         return true;
20865     },
20866
20867     // private
20868     shouldDeactivate : function(e){
20869         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20870             if(this.menu && this.menu.isVisible()){
20871                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20872             }
20873             return true;
20874         }
20875         return false;
20876     },
20877
20878     // private
20879     deactivate : function(){
20880         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20881         this.hideMenu();
20882     },
20883
20884     // private
20885     expandMenu : function(autoActivate){
20886         if(!this.disabled && this.menu){
20887             clearTimeout(this.hideTimer);
20888             delete this.hideTimer;
20889             if(!this.menu.isVisible() && !this.showTimer){
20890                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20891             }else if (this.menu.isVisible() && autoActivate){
20892                 this.menu.tryActivate(0, 1);
20893             }
20894         }
20895     },
20896
20897     // private
20898     deferExpand : function(autoActivate){
20899         delete this.showTimer;
20900         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20901         if(autoActivate){
20902             this.menu.tryActivate(0, 1);
20903         }
20904     },
20905
20906     // private
20907     hideMenu : function(){
20908         clearTimeout(this.showTimer);
20909         delete this.showTimer;
20910         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20911             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20912         }
20913     },
20914
20915     // private
20916     deferHide : function(){
20917         delete this.hideTimer;
20918         this.menu.hide();
20919     }
20920 });/*
20921  * Based on:
20922  * Ext JS Library 1.1.1
20923  * Copyright(c) 2006-2007, Ext JS, LLC.
20924  *
20925  * Originally Released Under LGPL - original licence link has changed is not relivant.
20926  *
20927  * Fork - LGPL
20928  * <script type="text/javascript">
20929  */
20930  
20931 /**
20932  * @class Roo.menu.CheckItem
20933  * @extends Roo.menu.Item
20934  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20935  * @constructor
20936  * Creates a new CheckItem
20937  * @param {Object} config Configuration options
20938  */
20939 Roo.menu.CheckItem = function(config){
20940     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20941     this.addEvents({
20942         /**
20943          * @event beforecheckchange
20944          * Fires before the checked value is set, providing an opportunity to cancel if needed
20945          * @param {Roo.menu.CheckItem} this
20946          * @param {Boolean} checked The new checked value that will be set
20947          */
20948         "beforecheckchange" : true,
20949         /**
20950          * @event checkchange
20951          * Fires after the checked value has been set
20952          * @param {Roo.menu.CheckItem} this
20953          * @param {Boolean} checked The checked value that was set
20954          */
20955         "checkchange" : true
20956     });
20957     if(this.checkHandler){
20958         this.on('checkchange', this.checkHandler, this.scope);
20959     }
20960 };
20961 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20962     /**
20963      * @cfg {String} group
20964      * All check items with the same group name will automatically be grouped into a single-select
20965      * radio button group (defaults to '')
20966      */
20967     /**
20968      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20969      */
20970     itemCls : "x-menu-item x-menu-check-item",
20971     /**
20972      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20973      */
20974     groupClass : "x-menu-group-item",
20975
20976     /**
20977      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20978      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20979      * initialized with checked = true will be rendered as checked.
20980      */
20981     checked: false,
20982
20983     // private
20984     ctype: "Roo.menu.CheckItem",
20985
20986     // private
20987     onRender : function(c){
20988         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20989         if(this.group){
20990             this.el.addClass(this.groupClass);
20991         }
20992         Roo.menu.MenuMgr.registerCheckable(this);
20993         if(this.checked){
20994             this.checked = false;
20995             this.setChecked(true, true);
20996         }
20997     },
20998
20999     // private
21000     destroy : function(){
21001         if(this.rendered){
21002             Roo.menu.MenuMgr.unregisterCheckable(this);
21003         }
21004         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
21005     },
21006
21007     /**
21008      * Set the checked state of this item
21009      * @param {Boolean} checked The new checked value
21010      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
21011      */
21012     setChecked : function(state, suppressEvent){
21013         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
21014             if(this.container){
21015                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
21016             }
21017             this.checked = state;
21018             if(suppressEvent !== true){
21019                 this.fireEvent("checkchange", this, state);
21020             }
21021         }
21022     },
21023
21024     // private
21025     handleClick : function(e){
21026        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
21027            this.setChecked(!this.checked);
21028        }
21029        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
21030     }
21031 });/*
21032  * Based on:
21033  * Ext JS Library 1.1.1
21034  * Copyright(c) 2006-2007, Ext JS, LLC.
21035  *
21036  * Originally Released Under LGPL - original licence link has changed is not relivant.
21037  *
21038  * Fork - LGPL
21039  * <script type="text/javascript">
21040  */
21041  
21042 /**
21043  * @class Roo.menu.DateItem
21044  * @extends Roo.menu.Adapter
21045  * A menu item that wraps the {@link Roo.DatPicker} component.
21046  * @constructor
21047  * Creates a new DateItem
21048  * @param {Object} config Configuration options
21049  */
21050 Roo.menu.DateItem = function(config){
21051     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
21052     /** The Roo.DatePicker object @type Roo.DatePicker */
21053     this.picker = this.component;
21054     this.addEvents({select: true});
21055     
21056     this.picker.on("render", function(picker){
21057         picker.getEl().swallowEvent("click");
21058         picker.container.addClass("x-menu-date-item");
21059     });
21060
21061     this.picker.on("select", this.onSelect, this);
21062 };
21063
21064 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21065     // private
21066     onSelect : function(picker, date){
21067         this.fireEvent("select", this, date, picker);
21068         Roo.menu.DateItem.superclass.handleClick.call(this);
21069     }
21070 });/*
21071  * Based on:
21072  * Ext JS Library 1.1.1
21073  * Copyright(c) 2006-2007, Ext JS, LLC.
21074  *
21075  * Originally Released Under LGPL - original licence link has changed is not relivant.
21076  *
21077  * Fork - LGPL
21078  * <script type="text/javascript">
21079  */
21080  
21081 /**
21082  * @class Roo.menu.ColorItem
21083  * @extends Roo.menu.Adapter
21084  * A menu item that wraps the {@link Roo.ColorPalette} component.
21085  * @constructor
21086  * Creates a new ColorItem
21087  * @param {Object} config Configuration options
21088  */
21089 Roo.menu.ColorItem = function(config){
21090     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21091     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21092     this.palette = this.component;
21093     this.relayEvents(this.palette, ["select"]);
21094     if(this.selectHandler){
21095         this.on('select', this.selectHandler, this.scope);
21096     }
21097 };
21098 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21099  * Based on:
21100  * Ext JS Library 1.1.1
21101  * Copyright(c) 2006-2007, Ext JS, LLC.
21102  *
21103  * Originally Released Under LGPL - original licence link has changed is not relivant.
21104  *
21105  * Fork - LGPL
21106  * <script type="text/javascript">
21107  */
21108  
21109
21110 /**
21111  * @class Roo.menu.DateMenu
21112  * @extends Roo.menu.Menu
21113  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21114  * @constructor
21115  * Creates a new DateMenu
21116  * @param {Object} config Configuration options
21117  */
21118 Roo.menu.DateMenu = function(config){
21119     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21120     this.plain = true;
21121     var di = new Roo.menu.DateItem(config);
21122     this.add(di);
21123     /**
21124      * The {@link Roo.DatePicker} instance for this DateMenu
21125      * @type DatePicker
21126      */
21127     this.picker = di.picker;
21128     /**
21129      * @event select
21130      * @param {DatePicker} picker
21131      * @param {Date} date
21132      */
21133     this.relayEvents(di, ["select"]);
21134
21135     this.on('beforeshow', function(){
21136         if(this.picker){
21137             this.picker.hideMonthPicker(true);
21138         }
21139     }, this);
21140 };
21141 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21142     cls:'x-date-menu'
21143 });/*
21144  * Based on:
21145  * Ext JS Library 1.1.1
21146  * Copyright(c) 2006-2007, Ext JS, LLC.
21147  *
21148  * Originally Released Under LGPL - original licence link has changed is not relivant.
21149  *
21150  * Fork - LGPL
21151  * <script type="text/javascript">
21152  */
21153  
21154
21155 /**
21156  * @class Roo.menu.ColorMenu
21157  * @extends Roo.menu.Menu
21158  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21159  * @constructor
21160  * Creates a new ColorMenu
21161  * @param {Object} config Configuration options
21162  */
21163 Roo.menu.ColorMenu = function(config){
21164     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21165     this.plain = true;
21166     var ci = new Roo.menu.ColorItem(config);
21167     this.add(ci);
21168     /**
21169      * The {@link Roo.ColorPalette} instance for this ColorMenu
21170      * @type ColorPalette
21171      */
21172     this.palette = ci.palette;
21173     /**
21174      * @event select
21175      * @param {ColorPalette} palette
21176      * @param {String} color
21177      */
21178     this.relayEvents(ci, ["select"]);
21179 };
21180 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21181  * Based on:
21182  * Ext JS Library 1.1.1
21183  * Copyright(c) 2006-2007, Ext JS, LLC.
21184  *
21185  * Originally Released Under LGPL - original licence link has changed is not relivant.
21186  *
21187  * Fork - LGPL
21188  * <script type="text/javascript">
21189  */
21190  
21191 /**
21192  * @class Roo.form.Field
21193  * @extends Roo.BoxComponent
21194  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21195  * @constructor
21196  * Creates a new Field
21197  * @param {Object} config Configuration options
21198  */
21199 Roo.form.Field = function(config){
21200     Roo.form.Field.superclass.constructor.call(this, config);
21201 };
21202
21203 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21204     /**
21205      * @cfg {String} fieldLabel Label to use when rendering a form.
21206      */
21207        /**
21208      * @cfg {String} qtip Mouse over tip
21209      */
21210      
21211     /**
21212      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21213      */
21214     invalidClass : "x-form-invalid",
21215     /**
21216      * @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")
21217      */
21218     invalidText : "The value in this field is invalid",
21219     /**
21220      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21221      */
21222     focusClass : "x-form-focus",
21223     /**
21224      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21225       automatic validation (defaults to "keyup").
21226      */
21227     validationEvent : "keyup",
21228     /**
21229      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21230      */
21231     validateOnBlur : true,
21232     /**
21233      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21234      */
21235     validationDelay : 250,
21236     /**
21237      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21238      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21239      */
21240     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21241     /**
21242      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21243      */
21244     fieldClass : "x-form-field",
21245     /**
21246      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21247      *<pre>
21248 Value         Description
21249 -----------   ----------------------------------------------------------------------
21250 qtip          Display a quick tip when the user hovers over the field
21251 title         Display a default browser title attribute popup
21252 under         Add a block div beneath the field containing the error text
21253 side          Add an error icon to the right of the field with a popup on hover
21254 [element id]  Add the error text directly to the innerHTML of the specified element
21255 </pre>
21256      */
21257     msgTarget : 'qtip',
21258     /**
21259      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21260      */
21261     msgFx : 'normal',
21262
21263     /**
21264      * @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.
21265      */
21266     readOnly : false,
21267
21268     /**
21269      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21270      */
21271     disabled : false,
21272
21273     /**
21274      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21275      */
21276     inputType : undefined,
21277     
21278     /**
21279      * @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).
21280          */
21281         tabIndex : undefined,
21282         
21283     // private
21284     isFormField : true,
21285
21286     // private
21287     hasFocus : false,
21288     /**
21289      * @property {Roo.Element} fieldEl
21290      * Element Containing the rendered Field (with label etc.)
21291      */
21292     /**
21293      * @cfg {Mixed} value A value to initialize this field with.
21294      */
21295     value : undefined,
21296
21297     /**
21298      * @cfg {String} name The field's HTML name attribute.
21299      */
21300     /**
21301      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21302      */
21303
21304         // private ??
21305         initComponent : function(){
21306         Roo.form.Field.superclass.initComponent.call(this);
21307         this.addEvents({
21308             /**
21309              * @event focus
21310              * Fires when this field receives input focus.
21311              * @param {Roo.form.Field} this
21312              */
21313             focus : true,
21314             /**
21315              * @event blur
21316              * Fires when this field loses input focus.
21317              * @param {Roo.form.Field} this
21318              */
21319             blur : true,
21320             /**
21321              * @event specialkey
21322              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21323              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21324              * @param {Roo.form.Field} this
21325              * @param {Roo.EventObject} e The event object
21326              */
21327             specialkey : true,
21328             /**
21329              * @event change
21330              * Fires just before the field blurs if the field value has changed.
21331              * @param {Roo.form.Field} this
21332              * @param {Mixed} newValue The new value
21333              * @param {Mixed} oldValue The original value
21334              */
21335             change : true,
21336             /**
21337              * @event invalid
21338              * Fires after the field has been marked as invalid.
21339              * @param {Roo.form.Field} this
21340              * @param {String} msg The validation message
21341              */
21342             invalid : true,
21343             /**
21344              * @event valid
21345              * Fires after the field has been validated with no errors.
21346              * @param {Roo.form.Field} this
21347              */
21348             valid : true,
21349              /**
21350              * @event keyup
21351              * Fires after the key up
21352              * @param {Roo.form.Field} this
21353              * @param {Roo.EventObject}  e The event Object
21354              */
21355             keyup : true
21356         });
21357     },
21358
21359     /**
21360      * Returns the name attribute of the field if available
21361      * @return {String} name The field name
21362      */
21363     getName: function(){
21364          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21365     },
21366
21367     // private
21368     onRender : function(ct, position){
21369         Roo.form.Field.superclass.onRender.call(this, ct, position);
21370         if(!this.el){
21371             var cfg = this.getAutoCreate();
21372             if(!cfg.name){
21373                 cfg.name = this.name || this.id;
21374             }
21375             if(this.inputType){
21376                 cfg.type = this.inputType;
21377             }
21378             this.el = ct.createChild(cfg, position);
21379         }
21380         var type = this.el.dom.type;
21381         if(type){
21382             if(type == 'password'){
21383                 type = 'text';
21384             }
21385             this.el.addClass('x-form-'+type);
21386         }
21387         if(this.readOnly){
21388             this.el.dom.readOnly = true;
21389         }
21390         if(this.tabIndex !== undefined){
21391             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21392         }
21393
21394         this.el.addClass([this.fieldClass, this.cls]);
21395         this.initValue();
21396     },
21397
21398     /**
21399      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21400      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21401      * @return {Roo.form.Field} this
21402      */
21403     applyTo : function(target){
21404         this.allowDomMove = false;
21405         this.el = Roo.get(target);
21406         this.render(this.el.dom.parentNode);
21407         return this;
21408     },
21409
21410     // private
21411     initValue : function(){
21412         if(this.value !== undefined){
21413             this.setValue(this.value);
21414         }else if(this.el.dom.value.length > 0){
21415             this.setValue(this.el.dom.value);
21416         }
21417     },
21418
21419     /**
21420      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21421      */
21422     isDirty : function() {
21423         if(this.disabled) {
21424             return false;
21425         }
21426         return String(this.getValue()) !== String(this.originalValue);
21427     },
21428
21429     // private
21430     afterRender : function(){
21431         Roo.form.Field.superclass.afterRender.call(this);
21432         this.initEvents();
21433     },
21434
21435     // private
21436     fireKey : function(e){
21437         //Roo.log('field ' + e.getKey());
21438         if(e.isNavKeyPress()){
21439             this.fireEvent("specialkey", this, e);
21440         }
21441     },
21442
21443     /**
21444      * Resets the current field value to the originally loaded value and clears any validation messages
21445      */
21446     reset : function(){
21447         this.setValue(this.originalValue);
21448         this.clearInvalid();
21449     },
21450
21451     // private
21452     initEvents : function(){
21453         // safari killled keypress - so keydown is now used..
21454         this.el.on("keydown" , this.fireKey,  this);
21455         this.el.on("focus", this.onFocus,  this);
21456         this.el.on("blur", this.onBlur,  this);
21457         this.el.relayEvent('keyup', this);
21458
21459         // reference to original value for reset
21460         this.originalValue = this.getValue();
21461     },
21462
21463     // private
21464     onFocus : function(){
21465         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21466             this.el.addClass(this.focusClass);
21467         }
21468         if(!this.hasFocus){
21469             this.hasFocus = true;
21470             this.startValue = this.getValue();
21471             this.fireEvent("focus", this);
21472         }
21473     },
21474
21475     beforeBlur : Roo.emptyFn,
21476
21477     // private
21478     onBlur : function(){
21479         this.beforeBlur();
21480         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21481             this.el.removeClass(this.focusClass);
21482         }
21483         this.hasFocus = false;
21484         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21485             this.validate();
21486         }
21487         var v = this.getValue();
21488         if(String(v) !== String(this.startValue)){
21489             this.fireEvent('change', this, v, this.startValue);
21490         }
21491         this.fireEvent("blur", this);
21492     },
21493
21494     /**
21495      * Returns whether or not the field value is currently valid
21496      * @param {Boolean} preventMark True to disable marking the field invalid
21497      * @return {Boolean} True if the value is valid, else false
21498      */
21499     isValid : function(preventMark){
21500         if(this.disabled){
21501             return true;
21502         }
21503         var restore = this.preventMark;
21504         this.preventMark = preventMark === true;
21505         var v = this.validateValue(this.processValue(this.getRawValue()));
21506         this.preventMark = restore;
21507         return v;
21508     },
21509
21510     /**
21511      * Validates the field value
21512      * @return {Boolean} True if the value is valid, else false
21513      */
21514     validate : function(){
21515         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21516             this.clearInvalid();
21517             return true;
21518         }
21519         return false;
21520     },
21521
21522     processValue : function(value){
21523         return value;
21524     },
21525
21526     // private
21527     // Subclasses should provide the validation implementation by overriding this
21528     validateValue : function(value){
21529         return true;
21530     },
21531
21532     /**
21533      * Mark this field as invalid
21534      * @param {String} msg The validation message
21535      */
21536     markInvalid : function(msg){
21537         if(!this.rendered || this.preventMark){ // not rendered
21538             return;
21539         }
21540         this.el.addClass(this.invalidClass);
21541         msg = msg || this.invalidText;
21542         switch(this.msgTarget){
21543             case 'qtip':
21544                 this.el.dom.qtip = msg;
21545                 this.el.dom.qclass = 'x-form-invalid-tip';
21546                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21547                     Roo.QuickTips.enable();
21548                 }
21549                 break;
21550             case 'title':
21551                 this.el.dom.title = msg;
21552                 break;
21553             case 'under':
21554                 if(!this.errorEl){
21555                     var elp = this.el.findParent('.x-form-element', 5, true);
21556                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21557                     this.errorEl.setWidth(elp.getWidth(true)-20);
21558                 }
21559                 this.errorEl.update(msg);
21560                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21561                 break;
21562             case 'side':
21563                 if(!this.errorIcon){
21564                     var elp = this.el.findParent('.x-form-element', 5, true);
21565                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21566                 }
21567                 this.alignErrorIcon();
21568                 this.errorIcon.dom.qtip = msg;
21569                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21570                 this.errorIcon.show();
21571                 this.on('resize', this.alignErrorIcon, this);
21572                 break;
21573             default:
21574                 var t = Roo.getDom(this.msgTarget);
21575                 t.innerHTML = msg;
21576                 t.style.display = this.msgDisplay;
21577                 break;
21578         }
21579         this.fireEvent('invalid', this, msg);
21580     },
21581
21582     // private
21583     alignErrorIcon : function(){
21584         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21585     },
21586
21587     /**
21588      * Clear any invalid styles/messages for this field
21589      */
21590     clearInvalid : function(){
21591         if(!this.rendered || this.preventMark){ // not rendered
21592             return;
21593         }
21594         this.el.removeClass(this.invalidClass);
21595         switch(this.msgTarget){
21596             case 'qtip':
21597                 this.el.dom.qtip = '';
21598                 break;
21599             case 'title':
21600                 this.el.dom.title = '';
21601                 break;
21602             case 'under':
21603                 if(this.errorEl){
21604                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21605                 }
21606                 break;
21607             case 'side':
21608                 if(this.errorIcon){
21609                     this.errorIcon.dom.qtip = '';
21610                     this.errorIcon.hide();
21611                     this.un('resize', this.alignErrorIcon, this);
21612                 }
21613                 break;
21614             default:
21615                 var t = Roo.getDom(this.msgTarget);
21616                 t.innerHTML = '';
21617                 t.style.display = 'none';
21618                 break;
21619         }
21620         this.fireEvent('valid', this);
21621     },
21622
21623     /**
21624      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21625      * @return {Mixed} value The field value
21626      */
21627     getRawValue : function(){
21628         var v = this.el.getValue();
21629         if(v === this.emptyText){
21630             v = '';
21631         }
21632         return v;
21633     },
21634
21635     /**
21636      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21637      * @return {Mixed} value The field value
21638      */
21639     getValue : function(){
21640         var v = this.el.getValue();
21641         if(v === this.emptyText || v === undefined){
21642             v = '';
21643         }
21644         return v;
21645     },
21646
21647     /**
21648      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21649      * @param {Mixed} value The value to set
21650      */
21651     setRawValue : function(v){
21652         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21653     },
21654
21655     /**
21656      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21657      * @param {Mixed} value The value to set
21658      */
21659     setValue : function(v){
21660         this.value = v;
21661         if(this.rendered){
21662             this.el.dom.value = (v === null || v === undefined ? '' : v);
21663              this.validate();
21664         }
21665     },
21666
21667     adjustSize : function(w, h){
21668         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21669         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21670         return s;
21671     },
21672
21673     adjustWidth : function(tag, w){
21674         tag = tag.toLowerCase();
21675         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21676             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21677                 if(tag == 'input'){
21678                     return w + 2;
21679                 }
21680                 if(tag = 'textarea'){
21681                     return w-2;
21682                 }
21683             }else if(Roo.isOpera){
21684                 if(tag == 'input'){
21685                     return w + 2;
21686                 }
21687                 if(tag = 'textarea'){
21688                     return w-2;
21689                 }
21690             }
21691         }
21692         return w;
21693     }
21694 });
21695
21696
21697 // anything other than normal should be considered experimental
21698 Roo.form.Field.msgFx = {
21699     normal : {
21700         show: function(msgEl, f){
21701             msgEl.setDisplayed('block');
21702         },
21703
21704         hide : function(msgEl, f){
21705             msgEl.setDisplayed(false).update('');
21706         }
21707     },
21708
21709     slide : {
21710         show: function(msgEl, f){
21711             msgEl.slideIn('t', {stopFx:true});
21712         },
21713
21714         hide : function(msgEl, f){
21715             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21716         }
21717     },
21718
21719     slideRight : {
21720         show: function(msgEl, f){
21721             msgEl.fixDisplay();
21722             msgEl.alignTo(f.el, 'tl-tr');
21723             msgEl.slideIn('l', {stopFx:true});
21724         },
21725
21726         hide : function(msgEl, f){
21727             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21728         }
21729     }
21730 };/*
21731  * Based on:
21732  * Ext JS Library 1.1.1
21733  * Copyright(c) 2006-2007, Ext JS, LLC.
21734  *
21735  * Originally Released Under LGPL - original licence link has changed is not relivant.
21736  *
21737  * Fork - LGPL
21738  * <script type="text/javascript">
21739  */
21740  
21741
21742 /**
21743  * @class Roo.form.TextField
21744  * @extends Roo.form.Field
21745  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21746  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21747  * @constructor
21748  * Creates a new TextField
21749  * @param {Object} config Configuration options
21750  */
21751 Roo.form.TextField = function(config){
21752     Roo.form.TextField.superclass.constructor.call(this, config);
21753     this.addEvents({
21754         /**
21755          * @event autosize
21756          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21757          * according to the default logic, but this event provides a hook for the developer to apply additional
21758          * logic at runtime to resize the field if needed.
21759              * @param {Roo.form.Field} this This text field
21760              * @param {Number} width The new field width
21761              */
21762         autosize : true
21763     });
21764 };
21765
21766 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21767     /**
21768      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21769      */
21770     grow : false,
21771     /**
21772      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21773      */
21774     growMin : 30,
21775     /**
21776      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21777      */
21778     growMax : 800,
21779     /**
21780      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21781      */
21782     vtype : null,
21783     /**
21784      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21785      */
21786     maskRe : null,
21787     /**
21788      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21789      */
21790     disableKeyFilter : false,
21791     /**
21792      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21793      */
21794     allowBlank : true,
21795     /**
21796      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21797      */
21798     minLength : 0,
21799     /**
21800      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21801      */
21802     maxLength : Number.MAX_VALUE,
21803     /**
21804      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21805      */
21806     minLengthText : "The minimum length for this field is {0}",
21807     /**
21808      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21809      */
21810     maxLengthText : "The maximum length for this field is {0}",
21811     /**
21812      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21813      */
21814     selectOnFocus : false,
21815     /**
21816      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21817      */
21818     blankText : "This field is required",
21819     /**
21820      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21821      * If available, this function will be called only after the basic validators all return true, and will be passed the
21822      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21823      */
21824     validator : null,
21825     /**
21826      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21827      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21828      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21829      */
21830     regex : null,
21831     /**
21832      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21833      */
21834     regexText : "",
21835     /**
21836      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21837      */
21838     emptyText : null,
21839     /**
21840      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21841      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21842      */
21843     emptyClass : 'x-form-empty-field',
21844
21845     // private
21846     initEvents : function(){
21847         Roo.form.TextField.superclass.initEvents.call(this);
21848         if(this.validationEvent == 'keyup'){
21849             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21850             this.el.on('keyup', this.filterValidation, this);
21851         }
21852         else if(this.validationEvent !== false){
21853             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21854         }
21855         if(this.selectOnFocus || this.emptyText){
21856             this.on("focus", this.preFocus, this);
21857             if(this.emptyText){
21858                 this.on('blur', this.postBlur, this);
21859                 this.applyEmptyText();
21860             }
21861         }
21862         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21863             this.el.on("keypress", this.filterKeys, this);
21864         }
21865         if(this.grow){
21866             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21867             this.el.on("click", this.autoSize,  this);
21868         }
21869     },
21870
21871     processValue : function(value){
21872         if(this.stripCharsRe){
21873             var newValue = value.replace(this.stripCharsRe, '');
21874             if(newValue !== value){
21875                 this.setRawValue(newValue);
21876                 return newValue;
21877             }
21878         }
21879         return value;
21880     },
21881
21882     filterValidation : function(e){
21883         if(!e.isNavKeyPress()){
21884             this.validationTask.delay(this.validationDelay);
21885         }
21886     },
21887
21888     // private
21889     onKeyUp : function(e){
21890         if(!e.isNavKeyPress()){
21891             this.autoSize();
21892         }
21893     },
21894
21895     /**
21896      * Resets the current field value to the originally-loaded value and clears any validation messages.
21897      * Also adds emptyText and emptyClass if the original value was blank.
21898      */
21899     reset : function(){
21900         Roo.form.TextField.superclass.reset.call(this);
21901         this.applyEmptyText();
21902     },
21903
21904     applyEmptyText : function(){
21905         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21906             this.setRawValue(this.emptyText);
21907             this.el.addClass(this.emptyClass);
21908         }
21909     },
21910
21911     // private
21912     preFocus : function(){
21913         if(this.emptyText){
21914             if(this.el.dom.value == this.emptyText){
21915                 this.setRawValue('');
21916             }
21917             this.el.removeClass(this.emptyClass);
21918         }
21919         if(this.selectOnFocus){
21920             this.el.dom.select();
21921         }
21922     },
21923
21924     // private
21925     postBlur : function(){
21926         this.applyEmptyText();
21927     },
21928
21929     // private
21930     filterKeys : function(e){
21931         var k = e.getKey();
21932         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21933             return;
21934         }
21935         var c = e.getCharCode(), cc = String.fromCharCode(c);
21936         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21937             return;
21938         }
21939         if(!this.maskRe.test(cc)){
21940             e.stopEvent();
21941         }
21942     },
21943
21944     setValue : function(v){
21945         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21946             this.el.removeClass(this.emptyClass);
21947         }
21948         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21949         this.applyEmptyText();
21950         this.autoSize();
21951     },
21952
21953     /**
21954      * Validates a value according to the field's validation rules and marks the field as invalid
21955      * if the validation fails
21956      * @param {Mixed} value The value to validate
21957      * @return {Boolean} True if the value is valid, else false
21958      */
21959     validateValue : function(value){
21960         if(value.length < 1 || value === this.emptyText){ // if it's blank
21961              if(this.allowBlank){
21962                 this.clearInvalid();
21963                 return true;
21964              }else{
21965                 this.markInvalid(this.blankText);
21966                 return false;
21967              }
21968         }
21969         if(value.length < this.minLength){
21970             this.markInvalid(String.format(this.minLengthText, this.minLength));
21971             return false;
21972         }
21973         if(value.length > this.maxLength){
21974             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21975             return false;
21976         }
21977         if(this.vtype){
21978             var vt = Roo.form.VTypes;
21979             if(!vt[this.vtype](value, this)){
21980                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21981                 return false;
21982             }
21983         }
21984         if(typeof this.validator == "function"){
21985             var msg = this.validator(value);
21986             if(msg !== true){
21987                 this.markInvalid(msg);
21988                 return false;
21989             }
21990         }
21991         if(this.regex && !this.regex.test(value)){
21992             this.markInvalid(this.regexText);
21993             return false;
21994         }
21995         return true;
21996     },
21997
21998     /**
21999      * Selects text in this field
22000      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
22001      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
22002      */
22003     selectText : function(start, end){
22004         var v = this.getRawValue();
22005         if(v.length > 0){
22006             start = start === undefined ? 0 : start;
22007             end = end === undefined ? v.length : end;
22008             var d = this.el.dom;
22009             if(d.setSelectionRange){
22010                 d.setSelectionRange(start, end);
22011             }else if(d.createTextRange){
22012                 var range = d.createTextRange();
22013                 range.moveStart("character", start);
22014                 range.moveEnd("character", v.length-end);
22015                 range.select();
22016             }
22017         }
22018     },
22019
22020     /**
22021      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
22022      * This only takes effect if grow = true, and fires the autosize event.
22023      */
22024     autoSize : function(){
22025         if(!this.grow || !this.rendered){
22026             return;
22027         }
22028         if(!this.metrics){
22029             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
22030         }
22031         var el = this.el;
22032         var v = el.dom.value;
22033         var d = document.createElement('div');
22034         d.appendChild(document.createTextNode(v));
22035         v = d.innerHTML;
22036         d = null;
22037         v += "&#160;";
22038         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
22039         this.el.setWidth(w);
22040         this.fireEvent("autosize", this, w);
22041     }
22042 });/*
22043  * Based on:
22044  * Ext JS Library 1.1.1
22045  * Copyright(c) 2006-2007, Ext JS, LLC.
22046  *
22047  * Originally Released Under LGPL - original licence link has changed is not relivant.
22048  *
22049  * Fork - LGPL
22050  * <script type="text/javascript">
22051  */
22052  
22053 /**
22054  * @class Roo.form.Hidden
22055  * @extends Roo.form.TextField
22056  * Simple Hidden element used on forms 
22057  * 
22058  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
22059  * 
22060  * @constructor
22061  * Creates a new Hidden form element.
22062  * @param {Object} config Configuration options
22063  */
22064
22065
22066
22067 // easy hidden field...
22068 Roo.form.Hidden = function(config){
22069     Roo.form.Hidden.superclass.constructor.call(this, config);
22070 };
22071   
22072 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22073     fieldLabel:      '',
22074     inputType:      'hidden',
22075     width:          50,
22076     allowBlank:     true,
22077     labelSeparator: '',
22078     hidden:         true,
22079     itemCls :       'x-form-item-display-none'
22080
22081
22082 });
22083
22084
22085 /*
22086  * Based on:
22087  * Ext JS Library 1.1.1
22088  * Copyright(c) 2006-2007, Ext JS, LLC.
22089  *
22090  * Originally Released Under LGPL - original licence link has changed is not relivant.
22091  *
22092  * Fork - LGPL
22093  * <script type="text/javascript">
22094  */
22095  
22096 /**
22097  * @class Roo.form.TriggerField
22098  * @extends Roo.form.TextField
22099  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22100  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22101  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22102  * for which you can provide a custom implementation.  For example:
22103  * <pre><code>
22104 var trigger = new Roo.form.TriggerField();
22105 trigger.onTriggerClick = myTriggerFn;
22106 trigger.applyTo('my-field');
22107 </code></pre>
22108  *
22109  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22110  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22111  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22112  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22113  * @constructor
22114  * Create a new TriggerField.
22115  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22116  * to the base TextField)
22117  */
22118 Roo.form.TriggerField = function(config){
22119     this.mimicing = false;
22120     Roo.form.TriggerField.superclass.constructor.call(this, config);
22121 };
22122
22123 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22124     /**
22125      * @cfg {String} triggerClass A CSS class to apply to the trigger
22126      */
22127     /**
22128      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22129      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22130      */
22131     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22132     /**
22133      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22134      */
22135     hideTrigger:false,
22136
22137     /** @cfg {Boolean} grow @hide */
22138     /** @cfg {Number} growMin @hide */
22139     /** @cfg {Number} growMax @hide */
22140
22141     /**
22142      * @hide 
22143      * @method
22144      */
22145     autoSize: Roo.emptyFn,
22146     // private
22147     monitorTab : true,
22148     // private
22149     deferHeight : true,
22150
22151     
22152     actionMode : 'wrap',
22153     // private
22154     onResize : function(w, h){
22155         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22156         if(typeof w == 'number'){
22157             var x = w - this.trigger.getWidth();
22158             this.el.setWidth(this.adjustWidth('input', x));
22159             this.trigger.setStyle('left', x+'px');
22160         }
22161     },
22162
22163     // private
22164     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22165
22166     // private
22167     getResizeEl : function(){
22168         return this.wrap;
22169     },
22170
22171     // private
22172     getPositionEl : function(){
22173         return this.wrap;
22174     },
22175
22176     // private
22177     alignErrorIcon : function(){
22178         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22179     },
22180
22181     // private
22182     onRender : function(ct, position){
22183         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22184         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22185         this.trigger = this.wrap.createChild(this.triggerConfig ||
22186                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22187         if(this.hideTrigger){
22188             this.trigger.setDisplayed(false);
22189         }
22190         this.initTrigger();
22191         if(!this.width){
22192             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22193         }
22194     },
22195
22196     // private
22197     initTrigger : function(){
22198         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22199         this.trigger.addClassOnOver('x-form-trigger-over');
22200         this.trigger.addClassOnClick('x-form-trigger-click');
22201     },
22202
22203     // private
22204     onDestroy : function(){
22205         if(this.trigger){
22206             this.trigger.removeAllListeners();
22207             this.trigger.remove();
22208         }
22209         if(this.wrap){
22210             this.wrap.remove();
22211         }
22212         Roo.form.TriggerField.superclass.onDestroy.call(this);
22213     },
22214
22215     // private
22216     onFocus : function(){
22217         Roo.form.TriggerField.superclass.onFocus.call(this);
22218         if(!this.mimicing){
22219             this.wrap.addClass('x-trigger-wrap-focus');
22220             this.mimicing = true;
22221             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22222             if(this.monitorTab){
22223                 this.el.on("keydown", this.checkTab, this);
22224             }
22225         }
22226     },
22227
22228     // private
22229     checkTab : function(e){
22230         if(e.getKey() == e.TAB){
22231             this.triggerBlur();
22232         }
22233     },
22234
22235     // private
22236     onBlur : function(){
22237         // do nothing
22238     },
22239
22240     // private
22241     mimicBlur : function(e, t){
22242         if(!this.wrap.contains(t) && this.validateBlur()){
22243             this.triggerBlur();
22244         }
22245     },
22246
22247     // private
22248     triggerBlur : function(){
22249         this.mimicing = false;
22250         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22251         if(this.monitorTab){
22252             this.el.un("keydown", this.checkTab, this);
22253         }
22254         this.wrap.removeClass('x-trigger-wrap-focus');
22255         Roo.form.TriggerField.superclass.onBlur.call(this);
22256     },
22257
22258     // private
22259     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22260     validateBlur : function(e, t){
22261         return true;
22262     },
22263
22264     // private
22265     onDisable : function(){
22266         Roo.form.TriggerField.superclass.onDisable.call(this);
22267         if(this.wrap){
22268             this.wrap.addClass('x-item-disabled');
22269         }
22270     },
22271
22272     // private
22273     onEnable : function(){
22274         Roo.form.TriggerField.superclass.onEnable.call(this);
22275         if(this.wrap){
22276             this.wrap.removeClass('x-item-disabled');
22277         }
22278     },
22279
22280     // private
22281     onShow : function(){
22282         var ae = this.getActionEl();
22283         
22284         if(ae){
22285             ae.dom.style.display = '';
22286             ae.dom.style.visibility = 'visible';
22287         }
22288     },
22289
22290     // private
22291     
22292     onHide : function(){
22293         var ae = this.getActionEl();
22294         ae.dom.style.display = 'none';
22295     },
22296
22297     /**
22298      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22299      * by an implementing function.
22300      * @method
22301      * @param {EventObject} e
22302      */
22303     onTriggerClick : Roo.emptyFn
22304 });
22305
22306 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22307 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22308 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22309 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22310     initComponent : function(){
22311         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22312
22313         this.triggerConfig = {
22314             tag:'span', cls:'x-form-twin-triggers', cn:[
22315             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22316             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22317         ]};
22318     },
22319
22320     getTrigger : function(index){
22321         return this.triggers[index];
22322     },
22323
22324     initTrigger : function(){
22325         var ts = this.trigger.select('.x-form-trigger', true);
22326         this.wrap.setStyle('overflow', 'hidden');
22327         var triggerField = this;
22328         ts.each(function(t, all, index){
22329             t.hide = function(){
22330                 var w = triggerField.wrap.getWidth();
22331                 this.dom.style.display = 'none';
22332                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22333             };
22334             t.show = function(){
22335                 var w = triggerField.wrap.getWidth();
22336                 this.dom.style.display = '';
22337                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22338             };
22339             var triggerIndex = 'Trigger'+(index+1);
22340
22341             if(this['hide'+triggerIndex]){
22342                 t.dom.style.display = 'none';
22343             }
22344             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22345             t.addClassOnOver('x-form-trigger-over');
22346             t.addClassOnClick('x-form-trigger-click');
22347         }, this);
22348         this.triggers = ts.elements;
22349     },
22350
22351     onTrigger1Click : Roo.emptyFn,
22352     onTrigger2Click : Roo.emptyFn
22353 });/*
22354  * Based on:
22355  * Ext JS Library 1.1.1
22356  * Copyright(c) 2006-2007, Ext JS, LLC.
22357  *
22358  * Originally Released Under LGPL - original licence link has changed is not relivant.
22359  *
22360  * Fork - LGPL
22361  * <script type="text/javascript">
22362  */
22363  
22364 /**
22365  * @class Roo.form.TextArea
22366  * @extends Roo.form.TextField
22367  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22368  * support for auto-sizing.
22369  * @constructor
22370  * Creates a new TextArea
22371  * @param {Object} config Configuration options
22372  */
22373 Roo.form.TextArea = function(config){
22374     Roo.form.TextArea.superclass.constructor.call(this, config);
22375     // these are provided exchanges for backwards compat
22376     // minHeight/maxHeight were replaced by growMin/growMax to be
22377     // compatible with TextField growing config values
22378     if(this.minHeight !== undefined){
22379         this.growMin = this.minHeight;
22380     }
22381     if(this.maxHeight !== undefined){
22382         this.growMax = this.maxHeight;
22383     }
22384 };
22385
22386 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22387     /**
22388      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22389      */
22390     growMin : 60,
22391     /**
22392      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22393      */
22394     growMax: 1000,
22395     /**
22396      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22397      * in the field (equivalent to setting overflow: hidden, defaults to false)
22398      */
22399     preventScrollbars: false,
22400     /**
22401      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22402      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22403      */
22404
22405     // private
22406     onRender : function(ct, position){
22407         if(!this.el){
22408             this.defaultAutoCreate = {
22409                 tag: "textarea",
22410                 style:"width:300px;height:60px;",
22411                 autocomplete: "off"
22412             };
22413         }
22414         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22415         if(this.grow){
22416             this.textSizeEl = Roo.DomHelper.append(document.body, {
22417                 tag: "pre", cls: "x-form-grow-sizer"
22418             });
22419             if(this.preventScrollbars){
22420                 this.el.setStyle("overflow", "hidden");
22421             }
22422             this.el.setHeight(this.growMin);
22423         }
22424     },
22425
22426     onDestroy : function(){
22427         if(this.textSizeEl){
22428             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22429         }
22430         Roo.form.TextArea.superclass.onDestroy.call(this);
22431     },
22432
22433     // private
22434     onKeyUp : function(e){
22435         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22436             this.autoSize();
22437         }
22438     },
22439
22440     /**
22441      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22442      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22443      */
22444     autoSize : function(){
22445         if(!this.grow || !this.textSizeEl){
22446             return;
22447         }
22448         var el = this.el;
22449         var v = el.dom.value;
22450         var ts = this.textSizeEl;
22451
22452         ts.innerHTML = '';
22453         ts.appendChild(document.createTextNode(v));
22454         v = ts.innerHTML;
22455
22456         Roo.fly(ts).setWidth(this.el.getWidth());
22457         if(v.length < 1){
22458             v = "&#160;&#160;";
22459         }else{
22460             if(Roo.isIE){
22461                 v = v.replace(/\n/g, '<p>&#160;</p>');
22462             }
22463             v += "&#160;\n&#160;";
22464         }
22465         ts.innerHTML = v;
22466         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22467         if(h != this.lastHeight){
22468             this.lastHeight = h;
22469             this.el.setHeight(h);
22470             this.fireEvent("autosize", this, h);
22471         }
22472     }
22473 });/*
22474  * Based on:
22475  * Ext JS Library 1.1.1
22476  * Copyright(c) 2006-2007, Ext JS, LLC.
22477  *
22478  * Originally Released Under LGPL - original licence link has changed is not relivant.
22479  *
22480  * Fork - LGPL
22481  * <script type="text/javascript">
22482  */
22483  
22484
22485 /**
22486  * @class Roo.form.NumberField
22487  * @extends Roo.form.TextField
22488  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22489  * @constructor
22490  * Creates a new NumberField
22491  * @param {Object} config Configuration options
22492  */
22493 Roo.form.NumberField = function(config){
22494     Roo.form.NumberField.superclass.constructor.call(this, config);
22495 };
22496
22497 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22498     /**
22499      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22500      */
22501     fieldClass: "x-form-field x-form-num-field",
22502     /**
22503      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22504      */
22505     allowDecimals : true,
22506     /**
22507      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22508      */
22509     decimalSeparator : ".",
22510     /**
22511      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22512      */
22513     decimalPrecision : 2,
22514     /**
22515      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22516      */
22517     allowNegative : true,
22518     /**
22519      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22520      */
22521     minValue : Number.NEGATIVE_INFINITY,
22522     /**
22523      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22524      */
22525     maxValue : Number.MAX_VALUE,
22526     /**
22527      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22528      */
22529     minText : "The minimum value for this field is {0}",
22530     /**
22531      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22532      */
22533     maxText : "The maximum value for this field is {0}",
22534     /**
22535      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22536      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22537      */
22538     nanText : "{0} is not a valid number",
22539
22540     // private
22541     initEvents : function(){
22542         Roo.form.NumberField.superclass.initEvents.call(this);
22543         var allowed = "0123456789";
22544         if(this.allowDecimals){
22545             allowed += this.decimalSeparator;
22546         }
22547         if(this.allowNegative){
22548             allowed += "-";
22549         }
22550         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22551         var keyPress = function(e){
22552             var k = e.getKey();
22553             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22554                 return;
22555             }
22556             var c = e.getCharCode();
22557             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22558                 e.stopEvent();
22559             }
22560         };
22561         this.el.on("keypress", keyPress, this);
22562     },
22563
22564     // private
22565     validateValue : function(value){
22566         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22567             return false;
22568         }
22569         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22570              return true;
22571         }
22572         var num = this.parseValue(value);
22573         if(isNaN(num)){
22574             this.markInvalid(String.format(this.nanText, value));
22575             return false;
22576         }
22577         if(num < this.minValue){
22578             this.markInvalid(String.format(this.minText, this.minValue));
22579             return false;
22580         }
22581         if(num > this.maxValue){
22582             this.markInvalid(String.format(this.maxText, this.maxValue));
22583             return false;
22584         }
22585         return true;
22586     },
22587
22588     getValue : function(){
22589         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22590     },
22591
22592     // private
22593     parseValue : function(value){
22594         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22595         return isNaN(value) ? '' : value;
22596     },
22597
22598     // private
22599     fixPrecision : function(value){
22600         var nan = isNaN(value);
22601         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22602             return nan ? '' : value;
22603         }
22604         return parseFloat(value).toFixed(this.decimalPrecision);
22605     },
22606
22607     setValue : function(v){
22608         v = this.fixPrecision(v);
22609         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22610     },
22611
22612     // private
22613     decimalPrecisionFcn : function(v){
22614         return Math.floor(v);
22615     },
22616
22617     beforeBlur : function(){
22618         var v = this.parseValue(this.getRawValue());
22619         if(v){
22620             this.setValue(v);
22621         }
22622     }
22623 });/*
22624  * Based on:
22625  * Ext JS Library 1.1.1
22626  * Copyright(c) 2006-2007, Ext JS, LLC.
22627  *
22628  * Originally Released Under LGPL - original licence link has changed is not relivant.
22629  *
22630  * Fork - LGPL
22631  * <script type="text/javascript">
22632  */
22633  
22634 /**
22635  * @class Roo.form.DateField
22636  * @extends Roo.form.TriggerField
22637  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22638 * @constructor
22639 * Create a new DateField
22640 * @param {Object} config
22641  */
22642 Roo.form.DateField = function(config){
22643     Roo.form.DateField.superclass.constructor.call(this, config);
22644     
22645       this.addEvents({
22646          
22647         /**
22648          * @event select
22649          * Fires when a date is selected
22650              * @param {Roo.form.DateField} combo This combo box
22651              * @param {Date} date The date selected
22652              */
22653         'select' : true
22654          
22655     });
22656     
22657     
22658     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22659     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22660     this.ddMatch = null;
22661     if(this.disabledDates){
22662         var dd = this.disabledDates;
22663         var re = "(?:";
22664         for(var i = 0; i < dd.length; i++){
22665             re += dd[i];
22666             if(i != dd.length-1) re += "|";
22667         }
22668         this.ddMatch = new RegExp(re + ")");
22669     }
22670 };
22671
22672 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22673     /**
22674      * @cfg {String} format
22675      * The default date format string which can be overriden for localization support.  The format must be
22676      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22677      */
22678     format : "m/d/y",
22679     /**
22680      * @cfg {String} altFormats
22681      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22682      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22683      */
22684     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22685     /**
22686      * @cfg {Array} disabledDays
22687      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22688      */
22689     disabledDays : null,
22690     /**
22691      * @cfg {String} disabledDaysText
22692      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22693      */
22694     disabledDaysText : "Disabled",
22695     /**
22696      * @cfg {Array} disabledDates
22697      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22698      * expression so they are very powerful. Some examples:
22699      * <ul>
22700      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22701      * <li>["03/08", "09/16"] would disable those days for every year</li>
22702      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22703      * <li>["03/../2006"] would disable every day in March 2006</li>
22704      * <li>["^03"] would disable every day in every March</li>
22705      * </ul>
22706      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22707      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22708      */
22709     disabledDates : null,
22710     /**
22711      * @cfg {String} disabledDatesText
22712      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22713      */
22714     disabledDatesText : "Disabled",
22715     /**
22716      * @cfg {Date/String} minValue
22717      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22718      * valid format (defaults to null).
22719      */
22720     minValue : null,
22721     /**
22722      * @cfg {Date/String} maxValue
22723      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22724      * valid format (defaults to null).
22725      */
22726     maxValue : null,
22727     /**
22728      * @cfg {String} minText
22729      * The error text to display when the date in the cell is before minValue (defaults to
22730      * 'The date in this field must be after {minValue}').
22731      */
22732     minText : "The date in this field must be equal to or after {0}",
22733     /**
22734      * @cfg {String} maxText
22735      * The error text to display when the date in the cell is after maxValue (defaults to
22736      * 'The date in this field must be before {maxValue}').
22737      */
22738     maxText : "The date in this field must be equal to or before {0}",
22739     /**
22740      * @cfg {String} invalidText
22741      * The error text to display when the date in the field is invalid (defaults to
22742      * '{value} is not a valid date - it must be in the format {format}').
22743      */
22744     invalidText : "{0} is not a valid date - it must be in the format {1}",
22745     /**
22746      * @cfg {String} triggerClass
22747      * An additional CSS class used to style the trigger button.  The trigger will always get the
22748      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22749      * which displays a calendar icon).
22750      */
22751     triggerClass : 'x-form-date-trigger',
22752     
22753
22754     /**
22755      * @cfg {bool} useIso
22756      * if enabled, then the date field will use a hidden field to store the 
22757      * real value as iso formated date. default (false)
22758      */ 
22759     useIso : false,
22760     /**
22761      * @cfg {String/Object} autoCreate
22762      * A DomHelper element spec, or true for a default element spec (defaults to
22763      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22764      */ 
22765     // private
22766     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22767     
22768     // private
22769     hiddenField: false,
22770     
22771     onRender : function(ct, position)
22772     {
22773         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22774         if (this.useIso) {
22775             this.el.dom.removeAttribute('name'); 
22776             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22777                     'before', true);
22778             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22779             // prevent input submission
22780             this.hiddenName = this.name;
22781         }
22782             
22783             
22784     },
22785     
22786     // private
22787     validateValue : function(value)
22788     {
22789         value = this.formatDate(value);
22790         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22791             return false;
22792         }
22793         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22794              return true;
22795         }
22796         var svalue = value;
22797         value = this.parseDate(value);
22798         if(!value){
22799             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22800             return false;
22801         }
22802         var time = value.getTime();
22803         if(this.minValue && time < this.minValue.getTime()){
22804             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22805             return false;
22806         }
22807         if(this.maxValue && time > this.maxValue.getTime()){
22808             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22809             return false;
22810         }
22811         if(this.disabledDays){
22812             var day = value.getDay();
22813             for(var i = 0; i < this.disabledDays.length; i++) {
22814                 if(day === this.disabledDays[i]){
22815                     this.markInvalid(this.disabledDaysText);
22816                     return false;
22817                 }
22818             }
22819         }
22820         var fvalue = this.formatDate(value);
22821         if(this.ddMatch && this.ddMatch.test(fvalue)){
22822             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22823             return false;
22824         }
22825         return true;
22826     },
22827
22828     // private
22829     // Provides logic to override the default TriggerField.validateBlur which just returns true
22830     validateBlur : function(){
22831         return !this.menu || !this.menu.isVisible();
22832     },
22833
22834     /**
22835      * Returns the current date value of the date field.
22836      * @return {Date} The date value
22837      */
22838     getValue : function(){
22839         
22840         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22841     },
22842
22843     /**
22844      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22845      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22846      * (the default format used is "m/d/y").
22847      * <br />Usage:
22848      * <pre><code>
22849 //All of these calls set the same date value (May 4, 2006)
22850
22851 //Pass a date object:
22852 var dt = new Date('5/4/06');
22853 dateField.setValue(dt);
22854
22855 //Pass a date string (default format):
22856 dateField.setValue('5/4/06');
22857
22858 //Pass a date string (custom format):
22859 dateField.format = 'Y-m-d';
22860 dateField.setValue('2006-5-4');
22861 </code></pre>
22862      * @param {String/Date} date The date or valid date string
22863      */
22864     setValue : function(date){
22865         if (this.hiddenField) {
22866             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22867         }
22868         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22869     },
22870
22871     // private
22872     parseDate : function(value){
22873         if(!value || value instanceof Date){
22874             return value;
22875         }
22876         var v = Date.parseDate(value, this.format);
22877         if(!v && this.altFormats){
22878             if(!this.altFormatsArray){
22879                 this.altFormatsArray = this.altFormats.split("|");
22880             }
22881             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22882                 v = Date.parseDate(value, this.altFormatsArray[i]);
22883             }
22884         }
22885         return v;
22886     },
22887
22888     // private
22889     formatDate : function(date, fmt){
22890         return (!date || !(date instanceof Date)) ?
22891                date : date.dateFormat(fmt || this.format);
22892     },
22893
22894     // private
22895     menuListeners : {
22896         select: function(m, d){
22897             this.setValue(d);
22898             this.fireEvent('select', this, d);
22899         },
22900         show : function(){ // retain focus styling
22901             this.onFocus();
22902         },
22903         hide : function(){
22904             this.focus.defer(10, this);
22905             var ml = this.menuListeners;
22906             this.menu.un("select", ml.select,  this);
22907             this.menu.un("show", ml.show,  this);
22908             this.menu.un("hide", ml.hide,  this);
22909         }
22910     },
22911
22912     // private
22913     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22914     onTriggerClick : function(){
22915         if(this.disabled){
22916             return;
22917         }
22918         if(this.menu == null){
22919             this.menu = new Roo.menu.DateMenu();
22920         }
22921         Roo.apply(this.menu.picker,  {
22922             showClear: this.allowBlank,
22923             minDate : this.minValue,
22924             maxDate : this.maxValue,
22925             disabledDatesRE : this.ddMatch,
22926             disabledDatesText : this.disabledDatesText,
22927             disabledDays : this.disabledDays,
22928             disabledDaysText : this.disabledDaysText,
22929             format : this.format,
22930             minText : String.format(this.minText, this.formatDate(this.minValue)),
22931             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22932         });
22933         this.menu.on(Roo.apply({}, this.menuListeners, {
22934             scope:this
22935         }));
22936         this.menu.picker.setValue(this.getValue() || new Date());
22937         this.menu.show(this.el, "tl-bl?");
22938     },
22939
22940     beforeBlur : function(){
22941         var v = this.parseDate(this.getRawValue());
22942         if(v){
22943             this.setValue(v);
22944         }
22945     }
22946
22947     /** @cfg {Boolean} grow @hide */
22948     /** @cfg {Number} growMin @hide */
22949     /** @cfg {Number} growMax @hide */
22950     /**
22951      * @hide
22952      * @method autoSize
22953      */
22954 });/*
22955  * Based on:
22956  * Ext JS Library 1.1.1
22957  * Copyright(c) 2006-2007, Ext JS, LLC.
22958  *
22959  * Originally Released Under LGPL - original licence link has changed is not relivant.
22960  *
22961  * Fork - LGPL
22962  * <script type="text/javascript">
22963  */
22964  
22965
22966 /**
22967  * @class Roo.form.ComboBox
22968  * @extends Roo.form.TriggerField
22969  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22970  * @constructor
22971  * Create a new ComboBox.
22972  * @param {Object} config Configuration options
22973  */
22974 Roo.form.ComboBox = function(config){
22975     Roo.form.ComboBox.superclass.constructor.call(this, config);
22976     this.addEvents({
22977         /**
22978          * @event expand
22979          * Fires when the dropdown list is expanded
22980              * @param {Roo.form.ComboBox} combo This combo box
22981              */
22982         'expand' : true,
22983         /**
22984          * @event collapse
22985          * Fires when the dropdown list is collapsed
22986              * @param {Roo.form.ComboBox} combo This combo box
22987              */
22988         'collapse' : true,
22989         /**
22990          * @event beforeselect
22991          * Fires before a list item is selected. Return false to cancel the selection.
22992              * @param {Roo.form.ComboBox} combo This combo box
22993              * @param {Roo.data.Record} record The data record returned from the underlying store
22994              * @param {Number} index The index of the selected item in the dropdown list
22995              */
22996         'beforeselect' : true,
22997         /**
22998          * @event select
22999          * Fires when a list item is selected
23000              * @param {Roo.form.ComboBox} combo This combo box
23001              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23002              * @param {Number} index The index of the selected item in the dropdown list
23003              */
23004         'select' : true,
23005         /**
23006          * @event beforequery
23007          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23008          * The event object passed has these properties:
23009              * @param {Roo.form.ComboBox} combo This combo box
23010              * @param {String} query The query
23011              * @param {Boolean} forceAll true to force "all" query
23012              * @param {Boolean} cancel true to cancel the query
23013              * @param {Object} e The query event object
23014              */
23015         'beforequery': true,
23016          /**
23017          * @event add
23018          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23019              * @param {Roo.form.ComboBox} combo This combo box
23020              */
23021         'add' : true,
23022         /**
23023          * @event edit
23024          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23025              * @param {Roo.form.ComboBox} combo This combo box
23026              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23027              */
23028         'edit' : true
23029         
23030         
23031     });
23032     if(this.transform){
23033         this.allowDomMove = false;
23034         var s = Roo.getDom(this.transform);
23035         if(!this.hiddenName){
23036             this.hiddenName = s.name;
23037         }
23038         if(!this.store){
23039             this.mode = 'local';
23040             var d = [], opts = s.options;
23041             for(var i = 0, len = opts.length;i < len; i++){
23042                 var o = opts[i];
23043                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23044                 if(o.selected) {
23045                     this.value = value;
23046                 }
23047                 d.push([value, o.text]);
23048             }
23049             this.store = new Roo.data.SimpleStore({
23050                 'id': 0,
23051                 fields: ['value', 'text'],
23052                 data : d
23053             });
23054             this.valueField = 'value';
23055             this.displayField = 'text';
23056         }
23057         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23058         if(!this.lazyRender){
23059             this.target = true;
23060             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23061             s.parentNode.removeChild(s); // remove it
23062             this.render(this.el.parentNode);
23063         }else{
23064             s.parentNode.removeChild(s); // remove it
23065         }
23066
23067     }
23068     if (this.store) {
23069         this.store = Roo.factory(this.store, Roo.data);
23070     }
23071     
23072     this.selectedIndex = -1;
23073     if(this.mode == 'local'){
23074         if(config.queryDelay === undefined){
23075             this.queryDelay = 10;
23076         }
23077         if(config.minChars === undefined){
23078             this.minChars = 0;
23079         }
23080     }
23081 };
23082
23083 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23084     /**
23085      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23086      */
23087     /**
23088      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23089      * rendering into an Roo.Editor, defaults to false)
23090      */
23091     /**
23092      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23093      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23094      */
23095     /**
23096      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23097      */
23098     /**
23099      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23100      * the dropdown list (defaults to undefined, with no header element)
23101      */
23102
23103      /**
23104      * @cfg {String/Roo.Template} tpl The template to use to render the output
23105      */
23106      
23107     // private
23108     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23109     /**
23110      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23111      */
23112     listWidth: undefined,
23113     /**
23114      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23115      * mode = 'remote' or 'text' if mode = 'local')
23116      */
23117     displayField: undefined,
23118     /**
23119      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23120      * mode = 'remote' or 'value' if mode = 'local'). 
23121      * Note: use of a valueField requires the user make a selection
23122      * in order for a value to be mapped.
23123      */
23124     valueField: undefined,
23125     
23126     
23127     /**
23128      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23129      * field's data value (defaults to the underlying DOM element's name)
23130      */
23131     hiddenName: undefined,
23132     /**
23133      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23134      */
23135     listClass: '',
23136     /**
23137      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23138      */
23139     selectedClass: 'x-combo-selected',
23140     /**
23141      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23142      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23143      * which displays a downward arrow icon).
23144      */
23145     triggerClass : 'x-form-arrow-trigger',
23146     /**
23147      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23148      */
23149     shadow:'sides',
23150     /**
23151      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23152      * anchor positions (defaults to 'tl-bl')
23153      */
23154     listAlign: 'tl-bl?',
23155     /**
23156      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23157      */
23158     maxHeight: 300,
23159     /**
23160      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23161      * query specified by the allQuery config option (defaults to 'query')
23162      */
23163     triggerAction: 'query',
23164     /**
23165      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23166      * (defaults to 4, does not apply if editable = false)
23167      */
23168     minChars : 4,
23169     /**
23170      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23171      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23172      */
23173     typeAhead: false,
23174     /**
23175      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23176      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23177      */
23178     queryDelay: 500,
23179     /**
23180      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23181      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23182      */
23183     pageSize: 0,
23184     /**
23185      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23186      * when editable = true (defaults to false)
23187      */
23188     selectOnFocus:false,
23189     /**
23190      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23191      */
23192     queryParam: 'query',
23193     /**
23194      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23195      * when mode = 'remote' (defaults to 'Loading...')
23196      */
23197     loadingText: 'Loading...',
23198     /**
23199      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23200      */
23201     resizable: false,
23202     /**
23203      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23204      */
23205     handleHeight : 8,
23206     /**
23207      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23208      * traditional select (defaults to true)
23209      */
23210     editable: true,
23211     /**
23212      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23213      */
23214     allQuery: '',
23215     /**
23216      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23217      */
23218     mode: 'remote',
23219     /**
23220      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23221      * listWidth has a higher value)
23222      */
23223     minListWidth : 70,
23224     /**
23225      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23226      * allow the user to set arbitrary text into the field (defaults to false)
23227      */
23228     forceSelection:false,
23229     /**
23230      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23231      * if typeAhead = true (defaults to 250)
23232      */
23233     typeAheadDelay : 250,
23234     /**
23235      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23236      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23237      */
23238     valueNotFoundText : undefined,
23239     /**
23240      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23241      */
23242     blockFocus : false,
23243     
23244     /**
23245      * @cfg {Boolean} disableClear Disable showing of clear button.
23246      */
23247     disableClear : false,
23248     /**
23249      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23250      */
23251     alwaysQuery : false,
23252     
23253     //private
23254     addicon : false,
23255     editicon: false,
23256     
23257     // element that contains real text value.. (when hidden is used..)
23258      
23259     // private
23260     onRender : function(ct, position){
23261         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23262         if(this.hiddenName){
23263             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23264                     'before', true);
23265             this.hiddenField.value =
23266                 this.hiddenValue !== undefined ? this.hiddenValue :
23267                 this.value !== undefined ? this.value : '';
23268
23269             // prevent input submission
23270             this.el.dom.removeAttribute('name');
23271              
23272              
23273         }
23274         if(Roo.isGecko){
23275             this.el.dom.setAttribute('autocomplete', 'off');
23276         }
23277
23278         var cls = 'x-combo-list';
23279
23280         this.list = new Roo.Layer({
23281             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23282         });
23283
23284         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23285         this.list.setWidth(lw);
23286         this.list.swallowEvent('mousewheel');
23287         this.assetHeight = 0;
23288
23289         if(this.title){
23290             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23291             this.assetHeight += this.header.getHeight();
23292         }
23293
23294         this.innerList = this.list.createChild({cls:cls+'-inner'});
23295         this.innerList.on('mouseover', this.onViewOver, this);
23296         this.innerList.on('mousemove', this.onViewMove, this);
23297         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23298         
23299         if(this.allowBlank && !this.pageSize && !this.disableClear){
23300             this.footer = this.list.createChild({cls:cls+'-ft'});
23301             this.pageTb = new Roo.Toolbar(this.footer);
23302            
23303         }
23304         if(this.pageSize){
23305             this.footer = this.list.createChild({cls:cls+'-ft'});
23306             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23307                     {pageSize: this.pageSize});
23308             
23309         }
23310         
23311         if (this.pageTb && this.allowBlank && !this.disableClear) {
23312             var _this = this;
23313             this.pageTb.add(new Roo.Toolbar.Fill(), {
23314                 cls: 'x-btn-icon x-btn-clear',
23315                 text: '&#160;',
23316                 handler: function()
23317                 {
23318                     _this.collapse();
23319                     _this.clearValue();
23320                     _this.onSelect(false, -1);
23321                 }
23322             });
23323         }
23324         if (this.footer) {
23325             this.assetHeight += this.footer.getHeight();
23326         }
23327         
23328
23329         if(!this.tpl){
23330             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23331         }
23332
23333         this.view = new Roo.View(this.innerList, this.tpl, {
23334             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23335         });
23336
23337         this.view.on('click', this.onViewClick, this);
23338
23339         this.store.on('beforeload', this.onBeforeLoad, this);
23340         this.store.on('load', this.onLoad, this);
23341         this.store.on('loadexception', this.onLoadException, this);
23342
23343         if(this.resizable){
23344             this.resizer = new Roo.Resizable(this.list,  {
23345                pinned:true, handles:'se'
23346             });
23347             this.resizer.on('resize', function(r, w, h){
23348                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23349                 this.listWidth = w;
23350                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23351                 this.restrictHeight();
23352             }, this);
23353             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23354         }
23355         if(!this.editable){
23356             this.editable = true;
23357             this.setEditable(false);
23358         }  
23359         
23360         
23361         if (typeof(this.events.add.listeners) != 'undefined') {
23362             
23363             this.addicon = this.wrap.createChild(
23364                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23365        
23366             this.addicon.on('click', function(e) {
23367                 this.fireEvent('add', this);
23368             }, this);
23369         }
23370         if (typeof(this.events.edit.listeners) != 'undefined') {
23371             
23372             this.editicon = this.wrap.createChild(
23373                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23374             if (this.addicon) {
23375                 this.editicon.setStyle('margin-left', '40px');
23376             }
23377             this.editicon.on('click', function(e) {
23378                 
23379                 // we fire even  if inothing is selected..
23380                 this.fireEvent('edit', this, this.lastData );
23381                 
23382             }, this);
23383         }
23384         
23385         
23386         
23387     },
23388
23389     // private
23390     initEvents : function(){
23391         Roo.form.ComboBox.superclass.initEvents.call(this);
23392
23393         this.keyNav = new Roo.KeyNav(this.el, {
23394             "up" : function(e){
23395                 this.inKeyMode = true;
23396                 this.selectPrev();
23397             },
23398
23399             "down" : function(e){
23400                 if(!this.isExpanded()){
23401                     this.onTriggerClick();
23402                 }else{
23403                     this.inKeyMode = true;
23404                     this.selectNext();
23405                 }
23406             },
23407
23408             "enter" : function(e){
23409                 this.onViewClick();
23410                 //return true;
23411             },
23412
23413             "esc" : function(e){
23414                 this.collapse();
23415             },
23416
23417             "tab" : function(e){
23418                 this.onViewClick(false);
23419                 this.fireEvent("specialkey", this, e);
23420                 return true;
23421             },
23422
23423             scope : this,
23424
23425             doRelay : function(foo, bar, hname){
23426                 if(hname == 'down' || this.scope.isExpanded()){
23427                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23428                 }
23429                 return true;
23430             },
23431
23432             forceKeyDown: true
23433         });
23434         this.queryDelay = Math.max(this.queryDelay || 10,
23435                 this.mode == 'local' ? 10 : 250);
23436         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23437         if(this.typeAhead){
23438             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23439         }
23440         if(this.editable !== false){
23441             this.el.on("keyup", this.onKeyUp, this);
23442         }
23443         if(this.forceSelection){
23444             this.on('blur', this.doForce, this);
23445         }
23446     },
23447
23448     onDestroy : function(){
23449         if(this.view){
23450             this.view.setStore(null);
23451             this.view.el.removeAllListeners();
23452             this.view.el.remove();
23453             this.view.purgeListeners();
23454         }
23455         if(this.list){
23456             this.list.destroy();
23457         }
23458         if(this.store){
23459             this.store.un('beforeload', this.onBeforeLoad, this);
23460             this.store.un('load', this.onLoad, this);
23461             this.store.un('loadexception', this.onLoadException, this);
23462         }
23463         Roo.form.ComboBox.superclass.onDestroy.call(this);
23464     },
23465
23466     // private
23467     fireKey : function(e){
23468         if(e.isNavKeyPress() && !this.list.isVisible()){
23469             this.fireEvent("specialkey", this, e);
23470         }
23471     },
23472
23473     // private
23474     onResize: function(w, h){
23475         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23476         
23477         if(typeof w != 'number'){
23478             // we do not handle it!?!?
23479             return;
23480         }
23481         var tw = this.trigger.getWidth();
23482         tw += this.addicon ? this.addicon.getWidth() : 0;
23483         tw += this.editicon ? this.editicon.getWidth() : 0;
23484         var x = w - tw;
23485         this.el.setWidth( this.adjustWidth('input', x));
23486             
23487         this.trigger.setStyle('left', x+'px');
23488         
23489         if(this.list && this.listWidth === undefined){
23490             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23491             this.list.setWidth(lw);
23492             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23493         }
23494         
23495     
23496         
23497     },
23498
23499     /**
23500      * Allow or prevent the user from directly editing the field text.  If false is passed,
23501      * the user will only be able to select from the items defined in the dropdown list.  This method
23502      * is the runtime equivalent of setting the 'editable' config option at config time.
23503      * @param {Boolean} value True to allow the user to directly edit the field text
23504      */
23505     setEditable : function(value){
23506         if(value == this.editable){
23507             return;
23508         }
23509         this.editable = value;
23510         if(!value){
23511             this.el.dom.setAttribute('readOnly', true);
23512             this.el.on('mousedown', this.onTriggerClick,  this);
23513             this.el.addClass('x-combo-noedit');
23514         }else{
23515             this.el.dom.setAttribute('readOnly', false);
23516             this.el.un('mousedown', this.onTriggerClick,  this);
23517             this.el.removeClass('x-combo-noedit');
23518         }
23519     },
23520
23521     // private
23522     onBeforeLoad : function(){
23523         if(!this.hasFocus){
23524             return;
23525         }
23526         this.innerList.update(this.loadingText ?
23527                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23528         this.restrictHeight();
23529         this.selectedIndex = -1;
23530     },
23531
23532     // private
23533     onLoad : function(){
23534         if(!this.hasFocus){
23535             return;
23536         }
23537         if(this.store.getCount() > 0){
23538             this.expand();
23539             this.restrictHeight();
23540             if(this.lastQuery == this.allQuery){
23541                 if(this.editable){
23542                     this.el.dom.select();
23543                 }
23544                 if(!this.selectByValue(this.value, true)){
23545                     this.select(0, true);
23546                 }
23547             }else{
23548                 this.selectNext();
23549                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23550                     this.taTask.delay(this.typeAheadDelay);
23551                 }
23552             }
23553         }else{
23554             this.onEmptyResults();
23555         }
23556         //this.el.focus();
23557     },
23558     // private
23559     onLoadException : function()
23560     {
23561         this.collapse();
23562         Roo.log(this.store.reader.jsonData);
23563         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23564             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23565         }
23566         
23567         
23568     },
23569     // private
23570     onTypeAhead : function(){
23571         if(this.store.getCount() > 0){
23572             var r = this.store.getAt(0);
23573             var newValue = r.data[this.displayField];
23574             var len = newValue.length;
23575             var selStart = this.getRawValue().length;
23576             if(selStart != len){
23577                 this.setRawValue(newValue);
23578                 this.selectText(selStart, newValue.length);
23579             }
23580         }
23581     },
23582
23583     // private
23584     onSelect : function(record, index){
23585         if(this.fireEvent('beforeselect', this, record, index) !== false){
23586             this.setFromData(index > -1 ? record.data : false);
23587             this.collapse();
23588             this.fireEvent('select', this, record, index);
23589         }
23590     },
23591
23592     /**
23593      * Returns the currently selected field value or empty string if no value is set.
23594      * @return {String} value The selected value
23595      */
23596     getValue : function(){
23597         if(this.valueField){
23598             return typeof this.value != 'undefined' ? this.value : '';
23599         }else{
23600             return Roo.form.ComboBox.superclass.getValue.call(this);
23601         }
23602     },
23603
23604     /**
23605      * Clears any text/value currently set in the field
23606      */
23607     clearValue : function(){
23608         if(this.hiddenField){
23609             this.hiddenField.value = '';
23610         }
23611         this.value = '';
23612         this.setRawValue('');
23613         this.lastSelectionText = '';
23614         this.applyEmptyText();
23615     },
23616
23617     /**
23618      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23619      * will be displayed in the field.  If the value does not match the data value of an existing item,
23620      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23621      * Otherwise the field will be blank (although the value will still be set).
23622      * @param {String} value The value to match
23623      */
23624     setValue : function(v){
23625         var text = v;
23626         if(this.valueField){
23627             var r = this.findRecord(this.valueField, v);
23628             if(r){
23629                 text = r.data[this.displayField];
23630             }else if(this.valueNotFoundText !== undefined){
23631                 text = this.valueNotFoundText;
23632             }
23633         }
23634         this.lastSelectionText = text;
23635         if(this.hiddenField){
23636             this.hiddenField.value = v;
23637         }
23638         Roo.form.ComboBox.superclass.setValue.call(this, text);
23639         this.value = v;
23640     },
23641     /**
23642      * @property {Object} the last set data for the element
23643      */
23644     
23645     lastData : false,
23646     /**
23647      * Sets the value of the field based on a object which is related to the record format for the store.
23648      * @param {Object} value the value to set as. or false on reset?
23649      */
23650     setFromData : function(o){
23651         var dv = ''; // display value
23652         var vv = ''; // value value..
23653         this.lastData = o;
23654         if (this.displayField) {
23655             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23656         } else {
23657             // this is an error condition!!!
23658             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23659         }
23660         
23661         if(this.valueField){
23662             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23663         }
23664         if(this.hiddenField){
23665             this.hiddenField.value = vv;
23666             
23667             this.lastSelectionText = dv;
23668             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23669             this.value = vv;
23670             return;
23671         }
23672         // no hidden field.. - we store the value in 'value', but still display
23673         // display field!!!!
23674         this.lastSelectionText = dv;
23675         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23676         this.value = vv;
23677         
23678         
23679     },
23680     // private
23681     reset : function(){
23682         // overridden so that last data is reset..
23683         this.setValue(this.originalValue);
23684         this.clearInvalid();
23685         this.lastData = false;
23686     },
23687     // private
23688     findRecord : function(prop, value){
23689         var record;
23690         if(this.store.getCount() > 0){
23691             this.store.each(function(r){
23692                 if(r.data[prop] == value){
23693                     record = r;
23694                     return false;
23695                 }
23696                 return true;
23697             });
23698         }
23699         return record;
23700     },
23701     
23702     getName: function()
23703     {
23704         // returns hidden if it's set..
23705         if (!this.rendered) {return ''};
23706         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23707         
23708     },
23709     // private
23710     onViewMove : function(e, t){
23711         this.inKeyMode = false;
23712     },
23713
23714     // private
23715     onViewOver : function(e, t){
23716         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23717             return;
23718         }
23719         var item = this.view.findItemFromChild(t);
23720         if(item){
23721             var index = this.view.indexOf(item);
23722             this.select(index, false);
23723         }
23724     },
23725
23726     // private
23727     onViewClick : function(doFocus)
23728     {
23729         var index = this.view.getSelectedIndexes()[0];
23730         var r = this.store.getAt(index);
23731         if(r){
23732             this.onSelect(r, index);
23733         }
23734         if(doFocus !== false && !this.blockFocus){
23735             this.el.focus();
23736         }
23737     },
23738
23739     // private
23740     restrictHeight : function(){
23741         this.innerList.dom.style.height = '';
23742         var inner = this.innerList.dom;
23743         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23744         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23745         this.list.beginUpdate();
23746         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23747         this.list.alignTo(this.el, this.listAlign);
23748         this.list.endUpdate();
23749     },
23750
23751     // private
23752     onEmptyResults : function(){
23753         this.collapse();
23754     },
23755
23756     /**
23757      * Returns true if the dropdown list is expanded, else false.
23758      */
23759     isExpanded : function(){
23760         return this.list.isVisible();
23761     },
23762
23763     /**
23764      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23765      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23766      * @param {String} value The data value of the item to select
23767      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23768      * selected item if it is not currently in view (defaults to true)
23769      * @return {Boolean} True if the value matched an item in the list, else false
23770      */
23771     selectByValue : function(v, scrollIntoView){
23772         if(v !== undefined && v !== null){
23773             var r = this.findRecord(this.valueField || this.displayField, v);
23774             if(r){
23775                 this.select(this.store.indexOf(r), scrollIntoView);
23776                 return true;
23777             }
23778         }
23779         return false;
23780     },
23781
23782     /**
23783      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23784      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23785      * @param {Number} index The zero-based index of the list item to select
23786      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23787      * selected item if it is not currently in view (defaults to true)
23788      */
23789     select : function(index, scrollIntoView){
23790         this.selectedIndex = index;
23791         this.view.select(index);
23792         if(scrollIntoView !== false){
23793             var el = this.view.getNode(index);
23794             if(el){
23795                 this.innerList.scrollChildIntoView(el, false);
23796             }
23797         }
23798     },
23799
23800     // private
23801     selectNext : function(){
23802         var ct = this.store.getCount();
23803         if(ct > 0){
23804             if(this.selectedIndex == -1){
23805                 this.select(0);
23806             }else if(this.selectedIndex < ct-1){
23807                 this.select(this.selectedIndex+1);
23808             }
23809         }
23810     },
23811
23812     // private
23813     selectPrev : function(){
23814         var ct = this.store.getCount();
23815         if(ct > 0){
23816             if(this.selectedIndex == -1){
23817                 this.select(0);
23818             }else if(this.selectedIndex != 0){
23819                 this.select(this.selectedIndex-1);
23820             }
23821         }
23822     },
23823
23824     // private
23825     onKeyUp : function(e){
23826         if(this.editable !== false && !e.isSpecialKey()){
23827             this.lastKey = e.getKey();
23828             this.dqTask.delay(this.queryDelay);
23829         }
23830     },
23831
23832     // private
23833     validateBlur : function(){
23834         return !this.list || !this.list.isVisible();   
23835     },
23836
23837     // private
23838     initQuery : function(){
23839         this.doQuery(this.getRawValue());
23840     },
23841
23842     // private
23843     doForce : function(){
23844         if(this.el.dom.value.length > 0){
23845             this.el.dom.value =
23846                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23847             this.applyEmptyText();
23848         }
23849     },
23850
23851     /**
23852      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23853      * query allowing the query action to be canceled if needed.
23854      * @param {String} query The SQL query to execute
23855      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23856      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23857      * saved in the current store (defaults to false)
23858      */
23859     doQuery : function(q, forceAll){
23860         if(q === undefined || q === null){
23861             q = '';
23862         }
23863         var qe = {
23864             query: q,
23865             forceAll: forceAll,
23866             combo: this,
23867             cancel:false
23868         };
23869         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23870             return false;
23871         }
23872         q = qe.query;
23873         forceAll = qe.forceAll;
23874         if(forceAll === true || (q.length >= this.minChars)){
23875             if(this.lastQuery != q || this.alwaysQuery){
23876                 this.lastQuery = q;
23877                 if(this.mode == 'local'){
23878                     this.selectedIndex = -1;
23879                     if(forceAll){
23880                         this.store.clearFilter();
23881                     }else{
23882                         this.store.filter(this.displayField, q);
23883                     }
23884                     this.onLoad();
23885                 }else{
23886                     this.store.baseParams[this.queryParam] = q;
23887                     this.store.load({
23888                         params: this.getParams(q)
23889                     });
23890                     this.expand();
23891                 }
23892             }else{
23893                 this.selectedIndex = -1;
23894                 this.onLoad();   
23895             }
23896         }
23897     },
23898
23899     // private
23900     getParams : function(q){
23901         var p = {};
23902         //p[this.queryParam] = q;
23903         if(this.pageSize){
23904             p.start = 0;
23905             p.limit = this.pageSize;
23906         }
23907         return p;
23908     },
23909
23910     /**
23911      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23912      */
23913     collapse : function(){
23914         if(!this.isExpanded()){
23915             return;
23916         }
23917         this.list.hide();
23918         Roo.get(document).un('mousedown', this.collapseIf, this);
23919         Roo.get(document).un('mousewheel', this.collapseIf, this);
23920         if (!this.editable) {
23921             Roo.get(document).un('keydown', this.listKeyPress, this);
23922         }
23923         this.fireEvent('collapse', this);
23924     },
23925
23926     // private
23927     collapseIf : function(e){
23928         if(!e.within(this.wrap) && !e.within(this.list)){
23929             this.collapse();
23930         }
23931     },
23932
23933     /**
23934      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23935      */
23936     expand : function(){
23937         if(this.isExpanded() || !this.hasFocus){
23938             return;
23939         }
23940         this.list.alignTo(this.el, this.listAlign);
23941         this.list.show();
23942         Roo.get(document).on('mousedown', this.collapseIf, this);
23943         Roo.get(document).on('mousewheel', this.collapseIf, this);
23944         if (!this.editable) {
23945             Roo.get(document).on('keydown', this.listKeyPress, this);
23946         }
23947         
23948         this.fireEvent('expand', this);
23949     },
23950
23951     // private
23952     // Implements the default empty TriggerField.onTriggerClick function
23953     onTriggerClick : function(){
23954         if(this.disabled){
23955             return;
23956         }
23957         if(this.isExpanded()){
23958             this.collapse();
23959             if (!this.blockFocus) {
23960                 this.el.focus();
23961             }
23962             
23963         }else {
23964             this.hasFocus = true;
23965             if(this.triggerAction == 'all') {
23966                 this.doQuery(this.allQuery, true);
23967             } else {
23968                 this.doQuery(this.getRawValue());
23969             }
23970             if (!this.blockFocus) {
23971                 this.el.focus();
23972             }
23973         }
23974     },
23975     listKeyPress : function(e)
23976     {
23977         //Roo.log('listkeypress');
23978         // scroll to first matching element based on key pres..
23979         if (e.isSpecialKey()) {
23980             return false;
23981         }
23982         var k = String.fromCharCode(e.getKey()).toUpperCase();
23983         //Roo.log(k);
23984         var match  = false;
23985         var csel = this.view.getSelectedNodes();
23986         var cselitem = false;
23987         if (csel.length) {
23988             var ix = this.view.indexOf(csel[0]);
23989             cselitem  = this.store.getAt(ix);
23990             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23991                 cselitem = false;
23992             }
23993             
23994         }
23995         
23996         this.store.each(function(v) { 
23997             if (cselitem) {
23998                 // start at existing selection.
23999                 if (cselitem.id == v.id) {
24000                     cselitem = false;
24001                 }
24002                 return;
24003             }
24004                 
24005             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24006                 match = this.store.indexOf(v);
24007                 return false;
24008             }
24009         }, this);
24010         
24011         if (match === false) {
24012             return true; // no more action?
24013         }
24014         // scroll to?
24015         this.view.select(match);
24016         var sn = Roo.get(this.view.getSelectedNodes()[0])
24017         sn.scrollIntoView(sn.dom.parentNode, false);
24018     }
24019
24020     /** 
24021     * @cfg {Boolean} grow 
24022     * @hide 
24023     */
24024     /** 
24025     * @cfg {Number} growMin 
24026     * @hide 
24027     */
24028     /** 
24029     * @cfg {Number} growMax 
24030     * @hide 
24031     */
24032     /**
24033      * @hide
24034      * @method autoSize
24035      */
24036 });/*
24037  * Copyright(c) 2010-2012, Roo J Solutions Limited
24038  *
24039  * Licence LGPL
24040  *
24041  */
24042
24043 /**
24044  * @class Roo.form.ComboBoxArray
24045  * @extends Roo.form.TextField
24046  * A facebook style adder... for lists of email / people / countries  etc...
24047  * pick multiple items from a combo box, and shows each one.
24048  *
24049  *  Fred [x]  Brian [x]  [Pick another |v]
24050  *
24051  *
24052  *  For this to work: it needs various extra information
24053  *    - normal combo problay has
24054  *      name, hiddenName
24055  *    + displayField, valueField
24056  *
24057  *    For our purpose...
24058  *
24059  *
24060  *   If we change from 'extends' to wrapping...
24061  *   
24062  *  
24063  *
24064  
24065  
24066  * @constructor
24067  * Create a new ComboBoxArray.
24068  * @param {Object} config Configuration options
24069  */
24070  
24071
24072 Roo.form.ComboBoxArray = function(config)
24073 {
24074     
24075     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24076     
24077     this.items = new Roo.util.MixedCollection(false);
24078     
24079     // construct the child combo...
24080     
24081     
24082     
24083     
24084    
24085     
24086 }
24087
24088  
24089 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24090
24091     /**
24092      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24093      */
24094     
24095     lastData : false,
24096     
24097     // behavies liek a hiddne field
24098     inputType:      'hidden',
24099     /**
24100      * @cfg {Number} width The width of the box that displays the selected element
24101      */ 
24102     width:          300,
24103
24104     
24105     
24106     /**
24107      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24108      */
24109     name : false,
24110     /**
24111      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24112      */
24113     hiddenName : false,
24114     
24115     
24116     // private the array of items that are displayed..
24117     items  : false,
24118     // private - the hidden field el.
24119     hiddenEl : false,
24120     // private - the filed el..
24121     el : false,
24122     
24123     //validateValue : function() { return true; }, // all values are ok!
24124     //onAddClick: function() { },
24125     
24126     onRender : function(ct, position) 
24127     {
24128         
24129         // create the standard hidden element
24130         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24131         
24132         
24133         // give fake names to child combo;
24134         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24135         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24136         
24137         this.combo = Roo.factory(this.combo, Roo.form);
24138         this.combo.onRender(ct, position);
24139         
24140         // assigned so form know we need to do this..
24141         this.store          = this.combo.store;
24142         this.valueField     = this.combo.valueField;
24143         this.displayField   = this.combo.displayField ;
24144         
24145         
24146         this.combo.wrap.addClass('x-cbarray-grp');
24147         
24148         var cbwrap = this.combo.wrap.createChild(
24149             {tag: 'div', cls: 'x-cbarray-cb'},
24150             this.combo.el.dom
24151         );
24152         
24153              
24154         this.hiddenEl = this.combo.wrap.createChild({
24155             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24156         });
24157         this.el = this.combo.wrap.createChild({
24158             tag: 'input',  type:'hidden' , name: this.name, value : ''
24159         });
24160          //   this.el.dom.removeAttribute("name");
24161         
24162         
24163         this.outerWrap = this.combo.wrap;
24164         this.wrap = cbwrap;
24165         
24166         this.outerWrap.setWidth(this.width);
24167         this.outerWrap.dom.removeChild(this.el.dom);
24168         
24169         this.wrap.dom.appendChild(this.el.dom);
24170         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24171         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24172         
24173         this.combo.trigger.setStyle('position','relative');
24174         this.combo.trigger.setStyle('left', '0px');
24175         this.combo.trigger.setStyle('top', '2px');
24176         
24177         this.combo.el.setStyle('vertical-align', 'text-bottom');
24178         
24179         //this.trigger.setStyle('vertical-align', 'top');
24180         
24181         // this should use the code from combo really... on('add' ....)
24182         if (this.adder) {
24183             
24184         
24185             this.adder = this.outerWrap.createChild(
24186                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24187             var _t = this;
24188             this.adder.on('click', function(e) {
24189                 _t.fireEvent('adderclick', this, e);
24190             }, _t);
24191         }
24192         //var _t = this;
24193         //this.adder.on('click', this.onAddClick, _t);
24194         
24195         
24196         this.combo.on('select', function(cb, rec, ix) {
24197             this.addItem(rec.data);
24198             
24199             cb.setValue('');
24200             cb.el.dom.value = '';
24201             //cb.lastData = rec.data;
24202             // add to list
24203             
24204         }, this);
24205         
24206         
24207     },
24208     
24209     
24210     getName: function()
24211     {
24212         // returns hidden if it's set..
24213         if (!this.rendered) {return ''};
24214         return  this.hiddenName ? this.hiddenName : this.name;
24215         
24216     },
24217     
24218     
24219     onResize: function(w, h){
24220         
24221         return;
24222         // not sure if this is needed..
24223         //this.combo.onResize(w,h);
24224         
24225         if(typeof w != 'number'){
24226             // we do not handle it!?!?
24227             return;
24228         }
24229         var tw = this.combo.trigger.getWidth();
24230         tw += this.addicon ? this.addicon.getWidth() : 0;
24231         tw += this.editicon ? this.editicon.getWidth() : 0;
24232         var x = w - tw;
24233         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24234             
24235         this.combo.trigger.setStyle('left', '0px');
24236         
24237         if(this.list && this.listWidth === undefined){
24238             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24239             this.list.setWidth(lw);
24240             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24241         }
24242         
24243     
24244         
24245     },
24246     
24247     addItem: function(rec)
24248     {
24249         var valueField = this.combo.valueField;
24250         var displayField = this.combo.displayField;
24251         if (this.items.indexOfKey(rec[valueField]) > -1) {
24252             //console.log("GOT " + rec.data.id);
24253             return;
24254         }
24255         
24256         var x = new Roo.form.ComboBoxArray.Item({
24257             //id : rec[this.idField],
24258             data : rec,
24259             displayField : displayField ,
24260             tipField : displayField ,
24261             cb : this
24262         });
24263         // use the 
24264         this.items.add(rec[valueField],x);
24265         // add it before the element..
24266         this.updateHiddenEl();
24267         x.render(this.outerWrap, this.wrap.dom);
24268         // add the image handler..
24269     },
24270     
24271     updateHiddenEl : function()
24272     {
24273         this.validate();
24274         if (!this.hiddenEl) {
24275             return;
24276         }
24277         var ar = [];
24278         var idField = this.combo.valueField;
24279         
24280         this.items.each(function(f) {
24281             ar.push(f.data[idField]);
24282            
24283         });
24284         this.hiddenEl.dom.value = ar.join(',');
24285         this.validate();
24286     },
24287     
24288     reset : function()
24289     {
24290         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24291         this.items.each(function(f) {
24292            f.remove(); 
24293         });
24294         this.el.dom.value = '';
24295         if (this.hiddenEl) {
24296             this.hiddenEl.dom.value = '';
24297         }
24298         
24299     },
24300     getValue: function()
24301     {
24302         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24303     },
24304     setValue: function(v) // not a valid action - must use addItems..
24305     {
24306          
24307         this.reset();
24308         
24309         
24310         
24311         if (this.store.isLocal && (typeof(v) == 'string')) {
24312             // then we can use the store to find the values..
24313             // comma seperated at present.. this needs to allow JSON based encoding..
24314             this.hiddenEl.value  = v;
24315             var v_ar = [];
24316             Roo.each(v.split(','), function(k) {
24317                 Roo.log("CHECK " + this.valueField + ',' + k);
24318                 var li = this.store.query(this.valueField, k);
24319                 if (!li.length) {
24320                     return;
24321                 }
24322                 add = {};
24323                 add[this.valueField] = k;
24324                 add[this.displayField] = li.item(0).data[this.displayField];
24325                 
24326                 this.addItem(add);
24327             }, this) 
24328              
24329         }
24330         if (typeof(v) == 'object') {
24331             // then let's assume it's an array of objects..
24332             Roo.each(v, function(l) {
24333                 this.addItem(l);
24334             }, this);
24335              
24336         }
24337         
24338         
24339     },
24340     setFromData: function(v)
24341     {
24342         // this recieves an object, if setValues is called.
24343         this.reset();
24344         this.el.dom.value = v[this.displayField];
24345         this.hiddenEl.dom.value = v[this.valueField];
24346         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24347             return;
24348         }
24349         var kv = v[this.valueField];
24350         var dv = v[this.displayField];
24351         kv = typeof(kv) != 'string' ? '' : kv;
24352         dv = typeof(dv) != 'string' ? '' : dv;
24353         
24354         
24355         var keys = kv.split(',');
24356         var display = dv.split(',');
24357         for (var i = 0 ; i < keys.length; i++) {
24358             
24359             add = {};
24360             add[this.valueField] = keys[i];
24361             add[this.displayField] = display[i];
24362             this.addItem(add);
24363         }
24364       
24365         
24366     },
24367     
24368     
24369     validateValue : function(value){
24370         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24371         
24372     }
24373     
24374 });
24375
24376
24377
24378 /**
24379  * @class Roo.form.ComboBoxArray.Item
24380  * @extends Roo.BoxComponent
24381  * A selected item in the list
24382  *  Fred [x]  Brian [x]  [Pick another |v]
24383  * 
24384  * @constructor
24385  * Create a new item.
24386  * @param {Object} config Configuration options
24387  */
24388  
24389 Roo.form.ComboBoxArray.Item = function(config) {
24390     config.id = Roo.id();
24391     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24392 }
24393
24394 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24395     data : {},
24396     cb: false,
24397     displayField : false,
24398     tipField : false,
24399     
24400     
24401     defaultAutoCreate : {
24402         tag: 'div',
24403         cls: 'x-cbarray-item',
24404         cn : [ 
24405             { tag: 'div' },
24406             {
24407                 tag: 'img',
24408                 width:16,
24409                 height : 16,
24410                 src : Roo.BLANK_IMAGE_URL ,
24411                 align: 'center'
24412             }
24413         ]
24414         
24415     },
24416     
24417  
24418     onRender : function(ct, position)
24419     {
24420         Roo.form.Field.superclass.onRender.call(this, ct, position);
24421         
24422         if(!this.el){
24423             var cfg = this.getAutoCreate();
24424             this.el = ct.createChild(cfg, position);
24425         }
24426         
24427         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24428         
24429         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24430             this.cb.renderer(this.data) :
24431             String.format('{0}',this.data[this.displayField]);
24432         
24433             
24434         this.el.child('div').dom.setAttribute('qtip',
24435                         String.format('{0}',this.data[this.tipField])
24436         );
24437         
24438         this.el.child('img').on('click', this.remove, this);
24439         
24440     },
24441    
24442     remove : function()
24443     {
24444         
24445         this.cb.items.remove(this);
24446         this.el.child('img').un('click', this.remove, this);
24447         this.el.remove();
24448         this.cb.updateHiddenEl();
24449     }
24450     
24451     
24452 });/*
24453  * Based on:
24454  * Ext JS Library 1.1.1
24455  * Copyright(c) 2006-2007, Ext JS, LLC.
24456  *
24457  * Originally Released Under LGPL - original licence link has changed is not relivant.
24458  *
24459  * Fork - LGPL
24460  * <script type="text/javascript">
24461  */
24462 /**
24463  * @class Roo.form.Checkbox
24464  * @extends Roo.form.Field
24465  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24466  * @constructor
24467  * Creates a new Checkbox
24468  * @param {Object} config Configuration options
24469  */
24470 Roo.form.Checkbox = function(config){
24471     Roo.form.Checkbox.superclass.constructor.call(this, config);
24472     this.addEvents({
24473         /**
24474          * @event check
24475          * Fires when the checkbox is checked or unchecked.
24476              * @param {Roo.form.Checkbox} this This checkbox
24477              * @param {Boolean} checked The new checked value
24478              */
24479         check : true
24480     });
24481 };
24482
24483 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24484     /**
24485      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24486      */
24487     focusClass : undefined,
24488     /**
24489      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24490      */
24491     fieldClass: "x-form-field",
24492     /**
24493      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24494      */
24495     checked: false,
24496     /**
24497      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24498      * {tag: "input", type: "checkbox", autocomplete: "off"})
24499      */
24500     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24501     /**
24502      * @cfg {String} boxLabel The text that appears beside the checkbox
24503      */
24504     boxLabel : "",
24505     /**
24506      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24507      */  
24508     inputValue : '1',
24509     /**
24510      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24511      */
24512      valueOff: '0', // value when not checked..
24513
24514     actionMode : 'viewEl', 
24515     //
24516     // private
24517     itemCls : 'x-menu-check-item x-form-item',
24518     groupClass : 'x-menu-group-item',
24519     inputType : 'hidden',
24520     
24521     
24522     inSetChecked: false, // check that we are not calling self...
24523     
24524     inputElement: false, // real input element?
24525     basedOn: false, // ????
24526     
24527     isFormField: true, // not sure where this is needed!!!!
24528
24529     onResize : function(){
24530         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24531         if(!this.boxLabel){
24532             this.el.alignTo(this.wrap, 'c-c');
24533         }
24534     },
24535
24536     initEvents : function(){
24537         Roo.form.Checkbox.superclass.initEvents.call(this);
24538         this.el.on("click", this.onClick,  this);
24539         this.el.on("change", this.onClick,  this);
24540     },
24541
24542
24543     getResizeEl : function(){
24544         return this.wrap;
24545     },
24546
24547     getPositionEl : function(){
24548         return this.wrap;
24549     },
24550
24551     // private
24552     onRender : function(ct, position){
24553         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24554         /*
24555         if(this.inputValue !== undefined){
24556             this.el.dom.value = this.inputValue;
24557         }
24558         */
24559         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24560         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24561         var viewEl = this.wrap.createChild({ 
24562             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24563         this.viewEl = viewEl;   
24564         this.wrap.on('click', this.onClick,  this); 
24565         
24566         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24567         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24568         
24569         
24570         
24571         if(this.boxLabel){
24572             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24573         //    viewEl.on('click', this.onClick,  this); 
24574         }
24575         //if(this.checked){
24576             this.setChecked(this.checked);
24577         //}else{
24578             //this.checked = this.el.dom;
24579         //}
24580
24581     },
24582
24583     // private
24584     initValue : Roo.emptyFn,
24585
24586     /**
24587      * Returns the checked state of the checkbox.
24588      * @return {Boolean} True if checked, else false
24589      */
24590     getValue : function(){
24591         if(this.el){
24592             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24593         }
24594         return this.valueOff;
24595         
24596     },
24597
24598         // private
24599     onClick : function(){ 
24600         this.setChecked(!this.checked);
24601
24602         //if(this.el.dom.checked != this.checked){
24603         //    this.setValue(this.el.dom.checked);
24604        // }
24605     },
24606
24607     /**
24608      * Sets the checked state of the checkbox.
24609      * On is always based on a string comparison between inputValue and the param.
24610      * @param {Boolean/String} value - the value to set 
24611      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24612      */
24613     setValue : function(v,suppressEvent){
24614         
24615         
24616         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24617         //if(this.el && this.el.dom){
24618         //    this.el.dom.checked = this.checked;
24619         //    this.el.dom.defaultChecked = this.checked;
24620         //}
24621         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24622         //this.fireEvent("check", this, this.checked);
24623     },
24624     // private..
24625     setChecked : function(state,suppressEvent)
24626     {
24627         if (this.inSetChecked) {
24628             this.checked = state;
24629             return;
24630         }
24631         
24632     
24633         if(this.wrap){
24634             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24635         }
24636         this.checked = state;
24637         if(suppressEvent !== true){
24638             this.fireEvent('check', this, state);
24639         }
24640         this.inSetChecked = true;
24641         this.el.dom.value = state ? this.inputValue : this.valueOff;
24642         this.inSetChecked = false;
24643         
24644     },
24645     // handle setting of hidden value by some other method!!?!?
24646     setFromHidden: function()
24647     {
24648         if(!this.el){
24649             return;
24650         }
24651         //console.log("SET FROM HIDDEN");
24652         //alert('setFrom hidden');
24653         this.setValue(this.el.dom.value);
24654     },
24655     
24656     onDestroy : function()
24657     {
24658         if(this.viewEl){
24659             Roo.get(this.viewEl).remove();
24660         }
24661          
24662         Roo.form.Checkbox.superclass.onDestroy.call(this);
24663     }
24664
24665 });/*
24666  * Based on:
24667  * Ext JS Library 1.1.1
24668  * Copyright(c) 2006-2007, Ext JS, LLC.
24669  *
24670  * Originally Released Under LGPL - original licence link has changed is not relivant.
24671  *
24672  * Fork - LGPL
24673  * <script type="text/javascript">
24674  */
24675  
24676 /**
24677  * @class Roo.form.Radio
24678  * @extends Roo.form.Checkbox
24679  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24680  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24681  * @constructor
24682  * Creates a new Radio
24683  * @param {Object} config Configuration options
24684  */
24685 Roo.form.Radio = function(){
24686     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24687 };
24688 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24689     inputType: 'radio',
24690
24691     /**
24692      * If this radio is part of a group, it will return the selected value
24693      * @return {String}
24694      */
24695     getGroupValue : function(){
24696         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24697     }
24698 });//<script type="text/javascript">
24699
24700 /*
24701  * Ext JS Library 1.1.1
24702  * Copyright(c) 2006-2007, Ext JS, LLC.
24703  * licensing@extjs.com
24704  * 
24705  * http://www.extjs.com/license
24706  */
24707  
24708  /*
24709   * 
24710   * Known bugs:
24711   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
24712   * - IE ? - no idea how much works there.
24713   * 
24714   * 
24715   * 
24716   */
24717  
24718
24719 /**
24720  * @class Ext.form.HtmlEditor
24721  * @extends Ext.form.Field
24722  * Provides a lightweight HTML Editor component.
24723  *
24724  * This has been tested on Fireforx / Chrome.. IE may not be so great..
24725  * 
24726  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
24727  * supported by this editor.</b><br/><br/>
24728  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
24729  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24730  */
24731 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
24732       /**
24733      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24734      */
24735     toolbars : false,
24736     /**
24737      * @cfg {String} createLinkText The default text for the create link prompt
24738      */
24739     createLinkText : 'Please enter the URL for the link:',
24740     /**
24741      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
24742      */
24743     defaultLinkValue : 'http:/'+'/',
24744    
24745      /**
24746      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24747      *                        Roo.resizable.
24748      */
24749     resizable : false,
24750      /**
24751      * @cfg {Number} height (in pixels)
24752      */   
24753     height: 300,
24754    /**
24755      * @cfg {Number} width (in pixels)
24756      */   
24757     width: 500,
24758     
24759     /**
24760      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24761      * 
24762      */
24763     stylesheets: false,
24764     
24765     // id of frame..
24766     frameId: false,
24767     
24768     // private properties
24769     validationEvent : false,
24770     deferHeight: true,
24771     initialized : false,
24772     activated : false,
24773     sourceEditMode : false,
24774     onFocus : Roo.emptyFn,
24775     iframePad:3,
24776     hideMode:'offsets',
24777     
24778     defaultAutoCreate : { // modified by initCompnoent..
24779         tag: "textarea",
24780         style:"width:500px;height:300px;",
24781         autocomplete: "off"
24782     },
24783
24784     // private
24785     initComponent : function(){
24786         this.addEvents({
24787             /**
24788              * @event initialize
24789              * Fires when the editor is fully initialized (including the iframe)
24790              * @param {HtmlEditor} this
24791              */
24792             initialize: true,
24793             /**
24794              * @event activate
24795              * Fires when the editor is first receives the focus. Any insertion must wait
24796              * until after this event.
24797              * @param {HtmlEditor} this
24798              */
24799             activate: true,
24800              /**
24801              * @event beforesync
24802              * Fires before the textarea is updated with content from the editor iframe. Return false
24803              * to cancel the sync.
24804              * @param {HtmlEditor} this
24805              * @param {String} html
24806              */
24807             beforesync: true,
24808              /**
24809              * @event beforepush
24810              * Fires before the iframe editor is updated with content from the textarea. Return false
24811              * to cancel the push.
24812              * @param {HtmlEditor} this
24813              * @param {String} html
24814              */
24815             beforepush: true,
24816              /**
24817              * @event sync
24818              * Fires when the textarea is updated with content from the editor iframe.
24819              * @param {HtmlEditor} this
24820              * @param {String} html
24821              */
24822             sync: true,
24823              /**
24824              * @event push
24825              * Fires when the iframe editor is updated with content from the textarea.
24826              * @param {HtmlEditor} this
24827              * @param {String} html
24828              */
24829             push: true,
24830              /**
24831              * @event editmodechange
24832              * Fires when the editor switches edit modes
24833              * @param {HtmlEditor} this
24834              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24835              */
24836             editmodechange: true,
24837             /**
24838              * @event editorevent
24839              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24840              * @param {HtmlEditor} this
24841              */
24842             editorevent: true
24843         });
24844         this.defaultAutoCreate =  {
24845             tag: "textarea",
24846             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
24847             autocomplete: "off"
24848         };
24849     },
24850
24851     /**
24852      * Protected method that will not generally be called directly. It
24853      * is called when the editor creates its toolbar. Override this method if you need to
24854      * add custom toolbar buttons.
24855      * @param {HtmlEditor} editor
24856      */
24857     createToolbar : function(editor){
24858         if (!editor.toolbars || !editor.toolbars.length) {
24859             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24860         }
24861         
24862         for (var i =0 ; i < editor.toolbars.length;i++) {
24863             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24864             editor.toolbars[i].init(editor);
24865         }
24866          
24867         
24868     },
24869
24870     /**
24871      * Protected method that will not generally be called directly. It
24872      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24873      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24874      */
24875     getDocMarkup : function(){
24876         // body styles..
24877         var st = '';
24878         if (this.stylesheets === false) {
24879             
24880             Roo.get(document.head).select('style').each(function(node) {
24881                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24882             });
24883             
24884             Roo.get(document.head).select('link').each(function(node) { 
24885                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24886             });
24887             
24888         } else if (!this.stylesheets.length) {
24889                 // simple..
24890                 st = '<style type="text/css">' +
24891                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24892                    '</style>';
24893         } else {
24894             Roo.each(this.stylesheets, function(s) {
24895                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24896             });
24897             
24898         }
24899         
24900         st +=  '<style type="text/css">' +
24901             'IMG { cursor: pointer } ' +
24902         '</style>';
24903
24904         
24905         return '<html><head>' + st  +
24906             //<style type="text/css">' +
24907             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24908             //'</style>' +
24909             ' </head><body class="roo-htmleditor-body"></body></html>';
24910     },
24911
24912     // private
24913     onRender : function(ct, position)
24914     {
24915         var _t = this;
24916         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24917         this.el.dom.style.border = '0 none';
24918         this.el.dom.setAttribute('tabIndex', -1);
24919         this.el.addClass('x-hidden');
24920         if(Roo.isIE){ // fix IE 1px bogus margin
24921             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24922         }
24923         this.wrap = this.el.wrap({
24924             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24925         });
24926         
24927         if (this.resizable) {
24928             this.resizeEl = new Roo.Resizable(this.wrap, {
24929                 pinned : true,
24930                 wrap: true,
24931                 dynamic : true,
24932                 minHeight : this.height,
24933                 height: this.height,
24934                 handles : this.resizable,
24935                 width: this.width,
24936                 listeners : {
24937                     resize : function(r, w, h) {
24938                         _t.onResize(w,h); // -something
24939                     }
24940                 }
24941             });
24942             
24943         }
24944
24945         this.frameId = Roo.id();
24946         
24947         this.createToolbar(this);
24948         
24949       
24950         
24951         var iframe = this.wrap.createChild({
24952             tag: 'iframe',
24953             id: this.frameId,
24954             name: this.frameId,
24955             frameBorder : 'no',
24956             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24957         }, this.el
24958         );
24959         
24960        // console.log(iframe);
24961         //this.wrap.dom.appendChild(iframe);
24962
24963         this.iframe = iframe.dom;
24964
24965          this.assignDocWin();
24966         
24967         this.doc.designMode = 'on';
24968        
24969         this.doc.open();
24970         this.doc.write(this.getDocMarkup());
24971         this.doc.close();
24972
24973         
24974         var task = { // must defer to wait for browser to be ready
24975             run : function(){
24976                 //console.log("run task?" + this.doc.readyState);
24977                 this.assignDocWin();
24978                 if(this.doc.body || this.doc.readyState == 'complete'){
24979                     try {
24980                         this.doc.designMode="on";
24981                     } catch (e) {
24982                         return;
24983                     }
24984                     Roo.TaskMgr.stop(task);
24985                     this.initEditor.defer(10, this);
24986                 }
24987             },
24988             interval : 10,
24989             duration:10000,
24990             scope: this
24991         };
24992         Roo.TaskMgr.start(task);
24993
24994         if(!this.width){
24995             this.setSize(this.wrap.getSize());
24996         }
24997         if (this.resizeEl) {
24998             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24999             // should trigger onReize..
25000         }
25001     },
25002
25003     // private
25004     onResize : function(w, h)
25005     {
25006         //Roo.log('resize: ' +w + ',' + h );
25007         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25008         if(this.el && this.iframe){
25009             if(typeof w == 'number'){
25010                 var aw = w - this.wrap.getFrameWidth('lr');
25011                 this.el.setWidth(this.adjustWidth('textarea', aw));
25012                 this.iframe.style.width = aw + 'px';
25013             }
25014             if(typeof h == 'number'){
25015                 var tbh = 0;
25016                 for (var i =0; i < this.toolbars.length;i++) {
25017                     // fixme - ask toolbars for heights?
25018                     tbh += this.toolbars[i].tb.el.getHeight();
25019                     if (this.toolbars[i].footer) {
25020                         tbh += this.toolbars[i].footer.el.getHeight();
25021                     }
25022                 }
25023                 
25024                 
25025                 
25026                 
25027                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25028                 ah -= 5; // knock a few pixes off for look..
25029                 this.el.setHeight(this.adjustWidth('textarea', ah));
25030                 this.iframe.style.height = ah + 'px';
25031                 if(this.doc){
25032                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25033                 }
25034             }
25035         }
25036     },
25037
25038     /**
25039      * Toggles the editor between standard and source edit mode.
25040      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25041      */
25042     toggleSourceEdit : function(sourceEditMode){
25043         
25044         this.sourceEditMode = sourceEditMode === true;
25045         
25046         if(this.sourceEditMode){
25047           
25048             this.syncValue();
25049             this.iframe.className = 'x-hidden';
25050             this.el.removeClass('x-hidden');
25051             this.el.dom.removeAttribute('tabIndex');
25052             this.el.focus();
25053         }else{
25054              
25055             this.pushValue();
25056             this.iframe.className = '';
25057             this.el.addClass('x-hidden');
25058             this.el.dom.setAttribute('tabIndex', -1);
25059             this.deferFocus();
25060         }
25061         this.setSize(this.wrap.getSize());
25062         this.fireEvent('editmodechange', this, this.sourceEditMode);
25063     },
25064
25065     // private used internally
25066     createLink : function(){
25067         var url = prompt(this.createLinkText, this.defaultLinkValue);
25068         if(url && url != 'http:/'+'/'){
25069             this.relayCmd('createlink', url);
25070         }
25071     },
25072
25073     // private (for BoxComponent)
25074     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25075
25076     // private (for BoxComponent)
25077     getResizeEl : function(){
25078         return this.wrap;
25079     },
25080
25081     // private (for BoxComponent)
25082     getPositionEl : function(){
25083         return this.wrap;
25084     },
25085
25086     // private
25087     initEvents : function(){
25088         this.originalValue = this.getValue();
25089     },
25090
25091     /**
25092      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25093      * @method
25094      */
25095     markInvalid : Roo.emptyFn,
25096     /**
25097      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25098      * @method
25099      */
25100     clearInvalid : Roo.emptyFn,
25101
25102     setValue : function(v){
25103         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25104         this.pushValue();
25105     },
25106
25107     /**
25108      * Protected method that will not generally be called directly. If you need/want
25109      * custom HTML cleanup, this is the method you should override.
25110      * @param {String} html The HTML to be cleaned
25111      * return {String} The cleaned HTML
25112      */
25113     cleanHtml : function(html){
25114         html = String(html);
25115         if(html.length > 5){
25116             if(Roo.isSafari){ // strip safari nonsense
25117                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25118             }
25119         }
25120         if(html == '&nbsp;'){
25121             html = '';
25122         }
25123         return html;
25124     },
25125
25126     /**
25127      * Protected method that will not generally be called directly. Syncs the contents
25128      * of the editor iframe with the textarea.
25129      */
25130     syncValue : function(){
25131         if(this.initialized){
25132             var bd = (this.doc.body || this.doc.documentElement);
25133             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25134             var html = bd.innerHTML;
25135             if(Roo.isSafari){
25136                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25137                 var m = bs.match(/text-align:(.*?);/i);
25138                 if(m && m[1]){
25139                     html = '<div style="'+m[0]+'">' + html + '</div>';
25140                 }
25141             }
25142             html = this.cleanHtml(html);
25143             // fix up the special chars..
25144             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25145                 return "&#"+b.charCodeAt()+";" 
25146             });
25147             if(this.fireEvent('beforesync', this, html) !== false){
25148                 this.el.dom.value = html;
25149                 this.fireEvent('sync', this, html);
25150             }
25151         }
25152     },
25153
25154     /**
25155      * Protected method that will not generally be called directly. Pushes the value of the textarea
25156      * into the iframe editor.
25157      */
25158     pushValue : function(){
25159         if(this.initialized){
25160             var v = this.el.dom.value;
25161             if(v.length < 1){
25162                 v = '&#160;';
25163             }
25164             
25165             if(this.fireEvent('beforepush', this, v) !== false){
25166                 var d = (this.doc.body || this.doc.documentElement);
25167                 d.innerHTML = v;
25168                 this.cleanUpPaste();
25169                 this.el.dom.value = d.innerHTML;
25170                 this.fireEvent('push', this, v);
25171             }
25172         }
25173     },
25174
25175     // private
25176     deferFocus : function(){
25177         this.focus.defer(10, this);
25178     },
25179
25180     // doc'ed in Field
25181     focus : function(){
25182         if(this.win && !this.sourceEditMode){
25183             this.win.focus();
25184         }else{
25185             this.el.focus();
25186         }
25187     },
25188     
25189     assignDocWin: function()
25190     {
25191         var iframe = this.iframe;
25192         
25193          if(Roo.isIE){
25194             this.doc = iframe.contentWindow.document;
25195             this.win = iframe.contentWindow;
25196         } else {
25197             if (!Roo.get(this.frameId)) {
25198                 return;
25199             }
25200             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25201             this.win = Roo.get(this.frameId).dom.contentWindow;
25202         }
25203     },
25204     
25205     // private
25206     initEditor : function(){
25207         //console.log("INIT EDITOR");
25208         this.assignDocWin();
25209         
25210         
25211         
25212         this.doc.designMode="on";
25213         this.doc.open();
25214         this.doc.write(this.getDocMarkup());
25215         this.doc.close();
25216         
25217         var dbody = (this.doc.body || this.doc.documentElement);
25218         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25219         // this copies styles from the containing element into thsi one..
25220         // not sure why we need all of this..
25221         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25222         ss['background-attachment'] = 'fixed'; // w3c
25223         dbody.bgProperties = 'fixed'; // ie
25224         Roo.DomHelper.applyStyles(dbody, ss);
25225         Roo.EventManager.on(this.doc, {
25226             //'mousedown': this.onEditorEvent,
25227             'mouseup': this.onEditorEvent,
25228             'dblclick': this.onEditorEvent,
25229             'click': this.onEditorEvent,
25230             'keyup': this.onEditorEvent,
25231             buffer:100,
25232             scope: this
25233         });
25234         if(Roo.isGecko){
25235             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25236         }
25237         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25238             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25239         }
25240         this.initialized = true;
25241
25242         this.fireEvent('initialize', this);
25243         this.pushValue();
25244     },
25245
25246     // private
25247     onDestroy : function(){
25248         
25249         
25250         
25251         if(this.rendered){
25252             
25253             for (var i =0; i < this.toolbars.length;i++) {
25254                 // fixme - ask toolbars for heights?
25255                 this.toolbars[i].onDestroy();
25256             }
25257             
25258             this.wrap.dom.innerHTML = '';
25259             this.wrap.remove();
25260         }
25261     },
25262
25263     // private
25264     onFirstFocus : function(){
25265         
25266         this.assignDocWin();
25267         
25268         
25269         this.activated = true;
25270         for (var i =0; i < this.toolbars.length;i++) {
25271             this.toolbars[i].onFirstFocus();
25272         }
25273        
25274         if(Roo.isGecko){ // prevent silly gecko errors
25275             this.win.focus();
25276             var s = this.win.getSelection();
25277             if(!s.focusNode || s.focusNode.nodeType != 3){
25278                 var r = s.getRangeAt(0);
25279                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25280                 r.collapse(true);
25281                 this.deferFocus();
25282             }
25283             try{
25284                 this.execCmd('useCSS', true);
25285                 this.execCmd('styleWithCSS', false);
25286             }catch(e){}
25287         }
25288         this.fireEvent('activate', this);
25289     },
25290
25291     // private
25292     adjustFont: function(btn){
25293         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25294         //if(Roo.isSafari){ // safari
25295         //    adjust *= 2;
25296        // }
25297         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25298         if(Roo.isSafari){ // safari
25299             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25300             v =  (v < 10) ? 10 : v;
25301             v =  (v > 48) ? 48 : v;
25302             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25303             
25304         }
25305         
25306         
25307         v = Math.max(1, v+adjust);
25308         
25309         this.execCmd('FontSize', v  );
25310     },
25311
25312     onEditorEvent : function(e){
25313         this.fireEvent('editorevent', this, e);
25314       //  this.updateToolbar();
25315         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25316     },
25317
25318     insertTag : function(tg)
25319     {
25320         // could be a bit smarter... -> wrap the current selected tRoo..
25321         
25322         this.execCmd("formatblock",   tg);
25323         
25324     },
25325     
25326     insertText : function(txt)
25327     {
25328         
25329         
25330         range = this.createRange();
25331         range.deleteContents();
25332                //alert(Sender.getAttribute('label'));
25333                
25334         range.insertNode(this.doc.createTextNode(txt));
25335     } ,
25336     
25337     // private
25338     relayBtnCmd : function(btn){
25339         this.relayCmd(btn.cmd);
25340     },
25341
25342     /**
25343      * Executes a Midas editor command on the editor document and performs necessary focus and
25344      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25345      * @param {String} cmd The Midas command
25346      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25347      */
25348     relayCmd : function(cmd, value){
25349         this.win.focus();
25350         this.execCmd(cmd, value);
25351         this.fireEvent('editorevent', this);
25352         //this.updateToolbar();
25353         this.deferFocus();
25354     },
25355
25356     /**
25357      * Executes a Midas editor command directly on the editor document.
25358      * For visual commands, you should use {@link #relayCmd} instead.
25359      * <b>This should only be called after the editor is initialized.</b>
25360      * @param {String} cmd The Midas command
25361      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25362      */
25363     execCmd : function(cmd, value){
25364         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25365         this.syncValue();
25366     },
25367  
25368  
25369    
25370     /**
25371      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25372      * to insert tRoo.
25373      * @param {String} text | dom node.. 
25374      */
25375     insertAtCursor : function(text)
25376     {
25377         
25378         
25379         
25380         if(!this.activated){
25381             return;
25382         }
25383         /*
25384         if(Roo.isIE){
25385             this.win.focus();
25386             var r = this.doc.selection.createRange();
25387             if(r){
25388                 r.collapse(true);
25389                 r.pasteHTML(text);
25390                 this.syncValue();
25391                 this.deferFocus();
25392             
25393             }
25394             return;
25395         }
25396         */
25397         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25398             this.win.focus();
25399             
25400             
25401             // from jquery ui (MIT licenced)
25402             var range, node;
25403             var win = this.win;
25404             
25405             if (win.getSelection && win.getSelection().getRangeAt) {
25406                 range = win.getSelection().getRangeAt(0);
25407                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25408                 range.insertNode(node);
25409             } else if (win.document.selection && win.document.selection.createRange) {
25410                 // no firefox support
25411                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25412                 win.document.selection.createRange().pasteHTML(txt);
25413             } else {
25414                 // no firefox support
25415                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25416                 this.execCmd('InsertHTML', txt);
25417             } 
25418             
25419             this.syncValue();
25420             
25421             this.deferFocus();
25422         }
25423     },
25424  // private
25425     mozKeyPress : function(e){
25426         if(e.ctrlKey){
25427             var c = e.getCharCode(), cmd;
25428           
25429             if(c > 0){
25430                 c = String.fromCharCode(c).toLowerCase();
25431                 switch(c){
25432                     case 'b':
25433                         cmd = 'bold';
25434                         break;
25435                     case 'i':
25436                         cmd = 'italic';
25437                         break;
25438                     
25439                     case 'u':
25440                         cmd = 'underline';
25441                         break;
25442                     
25443                     case 'v':
25444                         this.cleanUpPaste.defer(100, this);
25445                         return;
25446                         
25447                 }
25448                 if(cmd){
25449                     this.win.focus();
25450                     this.execCmd(cmd);
25451                     this.deferFocus();
25452                     e.preventDefault();
25453                 }
25454                 
25455             }
25456         }
25457     },
25458
25459     // private
25460     fixKeys : function(){ // load time branching for fastest keydown performance
25461         if(Roo.isIE){
25462             return function(e){
25463                 var k = e.getKey(), r;
25464                 if(k == e.TAB){
25465                     e.stopEvent();
25466                     r = this.doc.selection.createRange();
25467                     if(r){
25468                         r.collapse(true);
25469                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25470                         this.deferFocus();
25471                     }
25472                     return;
25473                 }
25474                 
25475                 if(k == e.ENTER){
25476                     r = this.doc.selection.createRange();
25477                     if(r){
25478                         var target = r.parentElement();
25479                         if(!target || target.tagName.toLowerCase() != 'li'){
25480                             e.stopEvent();
25481                             r.pasteHTML('<br />');
25482                             r.collapse(false);
25483                             r.select();
25484                         }
25485                     }
25486                 }
25487                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25488                     this.cleanUpPaste.defer(100, this);
25489                     return;
25490                 }
25491                 
25492                 
25493             };
25494         }else if(Roo.isOpera){
25495             return function(e){
25496                 var k = e.getKey();
25497                 if(k == e.TAB){
25498                     e.stopEvent();
25499                     this.win.focus();
25500                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25501                     this.deferFocus();
25502                 }
25503                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25504                     this.cleanUpPaste.defer(100, this);
25505                     return;
25506                 }
25507                 
25508             };
25509         }else if(Roo.isSafari){
25510             return function(e){
25511                 var k = e.getKey();
25512                 
25513                 if(k == e.TAB){
25514                     e.stopEvent();
25515                     this.execCmd('InsertText','\t');
25516                     this.deferFocus();
25517                     return;
25518                 }
25519                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25520                     this.cleanUpPaste.defer(100, this);
25521                     return;
25522                 }
25523                 
25524              };
25525         }
25526     }(),
25527     
25528     getAllAncestors: function()
25529     {
25530         var p = this.getSelectedNode();
25531         var a = [];
25532         if (!p) {
25533             a.push(p); // push blank onto stack..
25534             p = this.getParentElement();
25535         }
25536         
25537         
25538         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25539             a.push(p);
25540             p = p.parentNode;
25541         }
25542         a.push(this.doc.body);
25543         return a;
25544     },
25545     lastSel : false,
25546     lastSelNode : false,
25547     
25548     
25549     getSelection : function() 
25550     {
25551         this.assignDocWin();
25552         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25553     },
25554     
25555     getSelectedNode: function() 
25556     {
25557         // this may only work on Gecko!!!
25558         
25559         // should we cache this!!!!
25560         
25561         
25562         
25563          
25564         var range = this.createRange(this.getSelection()).cloneRange();
25565         
25566         if (Roo.isIE) {
25567             var parent = range.parentElement();
25568             while (true) {
25569                 var testRange = range.duplicate();
25570                 testRange.moveToElementText(parent);
25571                 if (testRange.inRange(range)) {
25572                     break;
25573                 }
25574                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25575                     break;
25576                 }
25577                 parent = parent.parentElement;
25578             }
25579             return parent;
25580         }
25581         
25582         // is ancestor a text element.
25583         var ac =  range.commonAncestorContainer;
25584         if (ac.nodeType == 3) {
25585             ac = ac.parentNode;
25586         }
25587         
25588         var ar = ac.childNodes;
25589          
25590         var nodes = [];
25591         var other_nodes = [];
25592         var has_other_nodes = false;
25593         for (var i=0;i<ar.length;i++) {
25594             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25595                 continue;
25596             }
25597             // fullly contained node.
25598             
25599             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25600                 nodes.push(ar[i]);
25601                 continue;
25602             }
25603             
25604             // probably selected..
25605             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25606                 other_nodes.push(ar[i]);
25607                 continue;
25608             }
25609             // outer..
25610             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25611                 continue;
25612             }
25613             
25614             
25615             has_other_nodes = true;
25616         }
25617         if (!nodes.length && other_nodes.length) {
25618             nodes= other_nodes;
25619         }
25620         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25621             return false;
25622         }
25623         
25624         return nodes[0];
25625     },
25626     createRange: function(sel)
25627     {
25628         // this has strange effects when using with 
25629         // top toolbar - not sure if it's a great idea.
25630         //this.editor.contentWindow.focus();
25631         if (typeof sel != "undefined") {
25632             try {
25633                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25634             } catch(e) {
25635                 return this.doc.createRange();
25636             }
25637         } else {
25638             return this.doc.createRange();
25639         }
25640     },
25641     getParentElement: function()
25642     {
25643         
25644         this.assignDocWin();
25645         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25646         
25647         var range = this.createRange(sel);
25648          
25649         try {
25650             var p = range.commonAncestorContainer;
25651             while (p.nodeType == 3) { // text node
25652                 p = p.parentNode;
25653             }
25654             return p;
25655         } catch (e) {
25656             return null;
25657         }
25658     
25659     },
25660     /***
25661      *
25662      * Range intersection.. the hard stuff...
25663      *  '-1' = before
25664      *  '0' = hits..
25665      *  '1' = after.
25666      *         [ -- selected range --- ]
25667      *   [fail]                        [fail]
25668      *
25669      *    basically..
25670      *      if end is before start or  hits it. fail.
25671      *      if start is after end or hits it fail.
25672      *
25673      *   if either hits (but other is outside. - then it's not 
25674      *   
25675      *    
25676      **/
25677     
25678     
25679     // @see http://www.thismuchiknow.co.uk/?p=64.
25680     rangeIntersectsNode : function(range, node)
25681     {
25682         var nodeRange = node.ownerDocument.createRange();
25683         try {
25684             nodeRange.selectNode(node);
25685         } catch (e) {
25686             nodeRange.selectNodeContents(node);
25687         }
25688     
25689         var rangeStartRange = range.cloneRange();
25690         rangeStartRange.collapse(true);
25691     
25692         var rangeEndRange = range.cloneRange();
25693         rangeEndRange.collapse(false);
25694     
25695         var nodeStartRange = nodeRange.cloneRange();
25696         nodeStartRange.collapse(true);
25697     
25698         var nodeEndRange = nodeRange.cloneRange();
25699         nodeEndRange.collapse(false);
25700     
25701         return rangeStartRange.compareBoundaryPoints(
25702                  Range.START_TO_START, nodeEndRange) == -1 &&
25703                rangeEndRange.compareBoundaryPoints(
25704                  Range.START_TO_START, nodeStartRange) == 1;
25705         
25706          
25707     },
25708     rangeCompareNode : function(range, node)
25709     {
25710         var nodeRange = node.ownerDocument.createRange();
25711         try {
25712             nodeRange.selectNode(node);
25713         } catch (e) {
25714             nodeRange.selectNodeContents(node);
25715         }
25716         
25717         
25718         range.collapse(true);
25719     
25720         nodeRange.collapse(true);
25721      
25722         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25723         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25724          
25725         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25726         
25727         var nodeIsBefore   =  ss == 1;
25728         var nodeIsAfter    = ee == -1;
25729         
25730         if (nodeIsBefore && nodeIsAfter)
25731             return 0; // outer
25732         if (!nodeIsBefore && nodeIsAfter)
25733             return 1; //right trailed.
25734         
25735         if (nodeIsBefore && !nodeIsAfter)
25736             return 2;  // left trailed.
25737         // fully contined.
25738         return 3;
25739     },
25740
25741     // private? - in a new class?
25742     cleanUpPaste :  function()
25743     {
25744         // cleans up the whole document..
25745          Roo.log('cleanuppaste');
25746         this.cleanUpChildren(this.doc.body);
25747         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25748         if (clean != this.doc.body.innerHTML) {
25749             this.doc.body.innerHTML = clean;
25750         }
25751         
25752     },
25753     
25754     cleanWordChars : function(input) {
25755         var he = Roo.form.HtmlEditor;
25756     
25757         var output = input;
25758         Roo.each(he.swapCodes, function(sw) { 
25759         
25760             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25761             output = output.replace(swapper, sw[1]);
25762         });
25763         return output;
25764     },
25765     
25766     
25767     cleanUpChildren : function (n)
25768     {
25769         if (!n.childNodes.length) {
25770             return;
25771         }
25772         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25773            this.cleanUpChild(n.childNodes[i]);
25774         }
25775     },
25776     
25777     
25778         
25779     
25780     cleanUpChild : function (node)
25781     {
25782         //console.log(node);
25783         if (node.nodeName == "#text") {
25784             // clean up silly Windows -- stuff?
25785             return; 
25786         }
25787         if (node.nodeName == "#comment") {
25788             node.parentNode.removeChild(node);
25789             // clean up silly Windows -- stuff?
25790             return; 
25791         }
25792         
25793         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
25794             // remove node.
25795             node.parentNode.removeChild(node);
25796             return;
25797             
25798         }
25799         
25800         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
25801         
25802         // remove <a name=....> as rendering on yahoo mailer is bored with this.
25803         
25804         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25805             remove_keep_children = true;
25806         }
25807         
25808         if (remove_keep_children) {
25809             this.cleanUpChildren(node);
25810             // inserts everything just before this node...
25811             while (node.childNodes.length) {
25812                 var cn = node.childNodes[0];
25813                 node.removeChild(cn);
25814                 node.parentNode.insertBefore(cn, node);
25815             }
25816             node.parentNode.removeChild(node);
25817             return;
25818         }
25819         
25820         if (!node.attributes || !node.attributes.length) {
25821             this.cleanUpChildren(node);
25822             return;
25823         }
25824         
25825         function cleanAttr(n,v)
25826         {
25827             
25828             if (v.match(/^\./) || v.match(/^\//)) {
25829                 return;
25830             }
25831             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25832                 return;
25833             }
25834             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
25835             node.removeAttribute(n);
25836             
25837         }
25838         
25839         function cleanStyle(n,v)
25840         {
25841             if (v.match(/expression/)) { //XSS?? should we even bother..
25842                 node.removeAttribute(n);
25843                 return;
25844             }
25845             
25846             
25847             var parts = v.split(/;/);
25848             Roo.each(parts, function(p) {
25849                 p = p.replace(/\s+/g,'');
25850                 if (!p.length) {
25851                     return true;
25852                 }
25853                 var l = p.split(':').shift().replace(/\s+/g,'');
25854                 
25855                 // only allow 'c whitelisted system attributes'
25856                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
25857                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
25858                     node.removeAttribute(n);
25859                     return false;
25860                 }
25861                 return true;
25862             });
25863             
25864             
25865         }
25866         
25867         
25868         for (var i = node.attributes.length-1; i > -1 ; i--) {
25869             var a = node.attributes[i];
25870             //console.log(a);
25871             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
25872                 node.removeAttribute(a.name);
25873                 return;
25874             }
25875             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
25876                 cleanAttr(a.name,a.value); // fixme..
25877                 return;
25878             }
25879             if (a.name == 'style') {
25880                 cleanStyle(a.name,a.value);
25881             }
25882             /// clean up MS crap..
25883             // tecnically this should be a list of valid class'es..
25884             
25885             
25886             if (a.name == 'class') {
25887                 if (a.value.match(/^Mso/)) {
25888                     node.className = '';
25889                 }
25890                 
25891                 if (a.value.match(/body/)) {
25892                     node.className = '';
25893                 }
25894             }
25895             
25896             // style cleanup!?
25897             // class cleanup?
25898             
25899         }
25900         
25901         
25902         this.cleanUpChildren(node);
25903         
25904         
25905     }
25906     
25907     
25908     // hide stuff that is not compatible
25909     /**
25910      * @event blur
25911      * @hide
25912      */
25913     /**
25914      * @event change
25915      * @hide
25916      */
25917     /**
25918      * @event focus
25919      * @hide
25920      */
25921     /**
25922      * @event specialkey
25923      * @hide
25924      */
25925     /**
25926      * @cfg {String} fieldClass @hide
25927      */
25928     /**
25929      * @cfg {String} focusClass @hide
25930      */
25931     /**
25932      * @cfg {String} autoCreate @hide
25933      */
25934     /**
25935      * @cfg {String} inputType @hide
25936      */
25937     /**
25938      * @cfg {String} invalidClass @hide
25939      */
25940     /**
25941      * @cfg {String} invalidText @hide
25942      */
25943     /**
25944      * @cfg {String} msgFx @hide
25945      */
25946     /**
25947      * @cfg {String} validateOnBlur @hide
25948      */
25949 });
25950
25951 Roo.form.HtmlEditor.white = [
25952         'area', 'br', 'img', 'input', 'hr', 'wbr',
25953         
25954        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25955        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25956        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25957        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25958        'table',   'ul',         'xmp', 
25959        
25960        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25961       'thead',   'tr', 
25962      
25963       'dir', 'menu', 'ol', 'ul', 'dl',
25964        
25965       'embed',  'object'
25966 ];
25967
25968
25969 Roo.form.HtmlEditor.black = [
25970     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25971         'applet', // 
25972         'base',   'basefont', 'bgsound', 'blink',  'body', 
25973         'frame',  'frameset', 'head',    'html',   'ilayer', 
25974         'iframe', 'layer',  'link',     'meta',    'object',   
25975         'script', 'style' ,'title',  'xml' // clean later..
25976 ];
25977 Roo.form.HtmlEditor.clean = [
25978     'script', 'style', 'title', 'xml'
25979 ];
25980 Roo.form.HtmlEditor.remove = [
25981     'font'
25982 ];
25983 // attributes..
25984
25985 Roo.form.HtmlEditor.ablack = [
25986     'on'
25987 ];
25988     
25989 Roo.form.HtmlEditor.aclean = [ 
25990     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25991 ];
25992
25993 // protocols..
25994 Roo.form.HtmlEditor.pwhite= [
25995         'http',  'https',  'mailto'
25996 ];
25997
25998 // white listed style attributes.
25999 Roo.form.HtmlEditor.cwhite= [
26000         'text-align',
26001         'font-size'
26002 ];
26003
26004
26005 Roo.form.HtmlEditor.swapCodes   =[ 
26006     [    8211, "--" ], 
26007     [    8212, "--" ], 
26008     [    8216,  "'" ],  
26009     [    8217, "'" ],  
26010     [    8220, '"' ],  
26011     [    8221, '"' ],  
26012     [    8226, "*" ],  
26013     [    8230, "..." ]
26014 ]; 
26015
26016     // <script type="text/javascript">
26017 /*
26018  * Based on
26019  * Ext JS Library 1.1.1
26020  * Copyright(c) 2006-2007, Ext JS, LLC.
26021  *  
26022  
26023  */
26024
26025 /**
26026  * @class Roo.form.HtmlEditorToolbar1
26027  * Basic Toolbar
26028  * 
26029  * Usage:
26030  *
26031  new Roo.form.HtmlEditor({
26032     ....
26033     toolbars : [
26034         new Roo.form.HtmlEditorToolbar1({
26035             disable : { fonts: 1 , format: 1, ..., ... , ...],
26036             btns : [ .... ]
26037         })
26038     }
26039      
26040  * 
26041  * @cfg {Object} disable List of elements to disable..
26042  * @cfg {Array} btns List of additional buttons.
26043  * 
26044  * 
26045  * NEEDS Extra CSS? 
26046  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26047  */
26048  
26049 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26050 {
26051     
26052     Roo.apply(this, config);
26053     
26054     // default disabled, based on 'good practice'..
26055     this.disable = this.disable || {};
26056     Roo.applyIf(this.disable, {
26057         fontSize : true,
26058         colors : true,
26059         specialElements : true
26060     });
26061     
26062     
26063     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26064     // dont call parent... till later.
26065 }
26066
26067 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26068     
26069     tb: false,
26070     
26071     rendered: false,
26072     
26073     editor : false,
26074     /**
26075      * @cfg {Object} disable  List of toolbar elements to disable
26076          
26077      */
26078     disable : false,
26079       /**
26080      * @cfg {Array} fontFamilies An array of available font families
26081      */
26082     fontFamilies : [
26083         'Arial',
26084         'Courier New',
26085         'Tahoma',
26086         'Times New Roman',
26087         'Verdana'
26088     ],
26089     
26090     specialChars : [
26091            "&#169;",
26092           "&#174;",     
26093           "&#8482;",    
26094           "&#163;" ,    
26095          // "&#8212;",    
26096           "&#8230;",    
26097           "&#247;" ,    
26098         //  "&#225;" ,     ?? a acute?
26099            "&#8364;"    , //Euro
26100        //   "&#8220;"    ,
26101         //  "&#8221;"    ,
26102         //  "&#8226;"    ,
26103           "&#176;"  //   , // degrees
26104
26105          // "&#233;"     , // e ecute
26106          // "&#250;"     , // u ecute?
26107     ],
26108     
26109     specialElements : [
26110         {
26111             text: "Insert Table",
26112             xtype: 'MenuItem',
26113             xns : Roo.Menu,
26114             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26115                 
26116         },
26117         {    
26118             text: "Insert Image",
26119             xtype: 'MenuItem',
26120             xns : Roo.Menu,
26121             ihtml : '<img src="about:blank"/>'
26122             
26123         }
26124         
26125          
26126     ],
26127     
26128     
26129     inputElements : [ 
26130             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26131             "input:submit", "input:button", "select", "textarea", "label" ],
26132     formats : [
26133         ["p"] ,  
26134         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26135         ["pre"],[ "code"], 
26136         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
26137     ],
26138      /**
26139      * @cfg {String} defaultFont default font to use.
26140      */
26141     defaultFont: 'tahoma',
26142    
26143     fontSelect : false,
26144     
26145     
26146     formatCombo : false,
26147     
26148     init : function(editor)
26149     {
26150         this.editor = editor;
26151         
26152         
26153         var fid = editor.frameId;
26154         var etb = this;
26155         function btn(id, toggle, handler){
26156             var xid = fid + '-'+ id ;
26157             return {
26158                 id : xid,
26159                 cmd : id,
26160                 cls : 'x-btn-icon x-edit-'+id,
26161                 enableToggle:toggle !== false,
26162                 scope: editor, // was editor...
26163                 handler:handler||editor.relayBtnCmd,
26164                 clickEvent:'mousedown',
26165                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26166                 tabIndex:-1
26167             };
26168         }
26169         
26170         
26171         
26172         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26173         this.tb = tb;
26174          // stop form submits
26175         tb.el.on('click', function(e){
26176             e.preventDefault(); // what does this do?
26177         });
26178
26179         if(!this.disable.font && !Roo.isSafari){
26180             /* why no safari for fonts
26181             editor.fontSelect = tb.el.createChild({
26182                 tag:'select',
26183                 tabIndex: -1,
26184                 cls:'x-font-select',
26185                 html: editor.createFontOptions()
26186             });
26187             editor.fontSelect.on('change', function(){
26188                 var font = editor.fontSelect.dom.value;
26189                 editor.relayCmd('fontname', font);
26190                 editor.deferFocus();
26191             }, editor);
26192             tb.add(
26193                 editor.fontSelect.dom,
26194                 '-'
26195             );
26196             */
26197         };
26198         if(!this.disable.formats){
26199             this.formatCombo = new Roo.form.ComboBox({
26200                 store: new Roo.data.SimpleStore({
26201                     id : 'tag',
26202                     fields: ['tag'],
26203                     data : this.formats // from states.js
26204                 }),
26205                 blockFocus : true,
26206                 //autoCreate : {tag: "div",  size: "20"},
26207                 displayField:'tag',
26208                 typeAhead: false,
26209                 mode: 'local',
26210                 editable : false,
26211                 triggerAction: 'all',
26212                 emptyText:'Add tag',
26213                 selectOnFocus:true,
26214                 width:135,
26215                 listeners : {
26216                     'select': function(c, r, i) {
26217                         editor.insertTag(r.get('tag'));
26218                         editor.focus();
26219                     }
26220                 }
26221
26222             });
26223             tb.addField(this.formatCombo);
26224             
26225         }
26226         
26227         if(!this.disable.format){
26228             tb.add(
26229                 btn('bold'),
26230                 btn('italic'),
26231                 btn('underline')
26232             );
26233         };
26234         if(!this.disable.fontSize){
26235             tb.add(
26236                 '-',
26237                 
26238                 
26239                 btn('increasefontsize', false, editor.adjustFont),
26240                 btn('decreasefontsize', false, editor.adjustFont)
26241             );
26242         };
26243         
26244         
26245         if(!this.disable.colors){
26246             tb.add(
26247                 '-', {
26248                     id:editor.frameId +'-forecolor',
26249                     cls:'x-btn-icon x-edit-forecolor',
26250                     clickEvent:'mousedown',
26251                     tooltip: this.buttonTips['forecolor'] || undefined,
26252                     tabIndex:-1,
26253                     menu : new Roo.menu.ColorMenu({
26254                         allowReselect: true,
26255                         focus: Roo.emptyFn,
26256                         value:'000000',
26257                         plain:true,
26258                         selectHandler: function(cp, color){
26259                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26260                             editor.deferFocus();
26261                         },
26262                         scope: editor,
26263                         clickEvent:'mousedown'
26264                     })
26265                 }, {
26266                     id:editor.frameId +'backcolor',
26267                     cls:'x-btn-icon x-edit-backcolor',
26268                     clickEvent:'mousedown',
26269                     tooltip: this.buttonTips['backcolor'] || undefined,
26270                     tabIndex:-1,
26271                     menu : new Roo.menu.ColorMenu({
26272                         focus: Roo.emptyFn,
26273                         value:'FFFFFF',
26274                         plain:true,
26275                         allowReselect: true,
26276                         selectHandler: function(cp, color){
26277                             if(Roo.isGecko){
26278                                 editor.execCmd('useCSS', false);
26279                                 editor.execCmd('hilitecolor', color);
26280                                 editor.execCmd('useCSS', true);
26281                                 editor.deferFocus();
26282                             }else{
26283                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26284                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26285                                 editor.deferFocus();
26286                             }
26287                         },
26288                         scope:editor,
26289                         clickEvent:'mousedown'
26290                     })
26291                 }
26292             );
26293         };
26294         // now add all the items...
26295         
26296
26297         if(!this.disable.alignments){
26298             tb.add(
26299                 '-',
26300                 btn('justifyleft'),
26301                 btn('justifycenter'),
26302                 btn('justifyright')
26303             );
26304         };
26305
26306         //if(!Roo.isSafari){
26307             if(!this.disable.links){
26308                 tb.add(
26309                     '-',
26310                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26311                 );
26312             };
26313
26314             if(!this.disable.lists){
26315                 tb.add(
26316                     '-',
26317                     btn('insertorderedlist'),
26318                     btn('insertunorderedlist')
26319                 );
26320             }
26321             if(!this.disable.sourceEdit){
26322                 tb.add(
26323                     '-',
26324                     btn('sourceedit', true, function(btn){
26325                         this.toggleSourceEdit(btn.pressed);
26326                     })
26327                 );
26328             }
26329         //}
26330         
26331         var smenu = { };
26332         // special menu.. - needs to be tidied up..
26333         if (!this.disable.special) {
26334             smenu = {
26335                 text: "&#169;",
26336                 cls: 'x-edit-none',
26337                 
26338                 menu : {
26339                     items : []
26340                 }
26341             };
26342             for (var i =0; i < this.specialChars.length; i++) {
26343                 smenu.menu.items.push({
26344                     
26345                     html: this.specialChars[i],
26346                     handler: function(a,b) {
26347                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26348                         //editor.insertAtCursor(a.html);
26349                         
26350                     },
26351                     tabIndex:-1
26352                 });
26353             }
26354             
26355             
26356             tb.add(smenu);
26357             
26358             
26359         }
26360          
26361         if (!this.disable.specialElements) {
26362             var semenu = {
26363                 text: "Other;",
26364                 cls: 'x-edit-none',
26365                 menu : {
26366                     items : []
26367                 }
26368             };
26369             for (var i =0; i < this.specialElements.length; i++) {
26370                 semenu.menu.items.push(
26371                     Roo.apply({ 
26372                         handler: function(a,b) {
26373                             editor.insertAtCursor(this.ihtml);
26374                         }
26375                     }, this.specialElements[i])
26376                 );
26377                     
26378             }
26379             
26380             tb.add(semenu);
26381             
26382             
26383         }
26384          
26385         
26386         if (this.btns) {
26387             for(var i =0; i< this.btns.length;i++) {
26388                 var b = Roo.factory(this.btns[i],Roo.form);
26389                 b.cls =  'x-edit-none';
26390                 b.scope = editor;
26391                 tb.add(b);
26392             }
26393         
26394         }
26395         
26396         
26397         
26398         // disable everything...
26399         
26400         this.tb.items.each(function(item){
26401            if(item.id != editor.frameId+ '-sourceedit'){
26402                 item.disable();
26403             }
26404         });
26405         this.rendered = true;
26406         
26407         // the all the btns;
26408         editor.on('editorevent', this.updateToolbar, this);
26409         // other toolbars need to implement this..
26410         //editor.on('editmodechange', this.updateToolbar, this);
26411     },
26412     
26413     
26414     
26415     /**
26416      * Protected method that will not generally be called directly. It triggers
26417      * a toolbar update by reading the markup state of the current selection in the editor.
26418      */
26419     updateToolbar: function(){
26420
26421         if(!this.editor.activated){
26422             this.editor.onFirstFocus();
26423             return;
26424         }
26425
26426         var btns = this.tb.items.map, 
26427             doc = this.editor.doc,
26428             frameId = this.editor.frameId;
26429
26430         if(!this.disable.font && !Roo.isSafari){
26431             /*
26432             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
26433             if(name != this.fontSelect.dom.value){
26434                 this.fontSelect.dom.value = name;
26435             }
26436             */
26437         }
26438         if(!this.disable.format){
26439             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
26440             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
26441             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
26442         }
26443         if(!this.disable.alignments){
26444             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
26445             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
26446             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
26447         }
26448         if(!Roo.isSafari && !this.disable.lists){
26449             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
26450             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
26451         }
26452         
26453         var ans = this.editor.getAllAncestors();
26454         if (this.formatCombo) {
26455             
26456             
26457             var store = this.formatCombo.store;
26458             this.formatCombo.setValue("");
26459             for (var i =0; i < ans.length;i++) {
26460                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26461                     // select it..
26462                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26463                     break;
26464                 }
26465             }
26466         }
26467         
26468         
26469         
26470         // hides menus... - so this cant be on a menu...
26471         Roo.menu.MenuMgr.hideAll();
26472
26473         //this.editorsyncValue();
26474     },
26475    
26476     
26477     createFontOptions : function(){
26478         var buf = [], fs = this.fontFamilies, ff, lc;
26479         for(var i = 0, len = fs.length; i< len; i++){
26480             ff = fs[i];
26481             lc = ff.toLowerCase();
26482             buf.push(
26483                 '<option value="',lc,'" style="font-family:',ff,';"',
26484                     (this.defaultFont == lc ? ' selected="true">' : '>'),
26485                     ff,
26486                 '</option>'
26487             );
26488         }
26489         return buf.join('');
26490     },
26491     
26492     toggleSourceEdit : function(sourceEditMode){
26493         if(sourceEditMode === undefined){
26494             sourceEditMode = !this.sourceEditMode;
26495         }
26496         this.sourceEditMode = sourceEditMode === true;
26497         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
26498         // just toggle the button?
26499         if(btn.pressed !== this.editor.sourceEditMode){
26500             btn.toggle(this.editor.sourceEditMode);
26501             return;
26502         }
26503         
26504         if(this.sourceEditMode){
26505             this.tb.items.each(function(item){
26506                 if(item.cmd != 'sourceedit'){
26507                     item.disable();
26508                 }
26509             });
26510           
26511         }else{
26512             if(this.initialized){
26513                 this.tb.items.each(function(item){
26514                     item.enable();
26515                 });
26516             }
26517             
26518         }
26519         // tell the editor that it's been pressed..
26520         this.editor.toggleSourceEdit(sourceEditMode);
26521        
26522     },
26523      /**
26524      * Object collection of toolbar tooltips for the buttons in the editor. The key
26525      * is the command id associated with that button and the value is a valid QuickTips object.
26526      * For example:
26527 <pre><code>
26528 {
26529     bold : {
26530         title: 'Bold (Ctrl+B)',
26531         text: 'Make the selected text bold.',
26532         cls: 'x-html-editor-tip'
26533     },
26534     italic : {
26535         title: 'Italic (Ctrl+I)',
26536         text: 'Make the selected text italic.',
26537         cls: 'x-html-editor-tip'
26538     },
26539     ...
26540 </code></pre>
26541     * @type Object
26542      */
26543     buttonTips : {
26544         bold : {
26545             title: 'Bold (Ctrl+B)',
26546             text: 'Make the selected text bold.',
26547             cls: 'x-html-editor-tip'
26548         },
26549         italic : {
26550             title: 'Italic (Ctrl+I)',
26551             text: 'Make the selected text italic.',
26552             cls: 'x-html-editor-tip'
26553         },
26554         underline : {
26555             title: 'Underline (Ctrl+U)',
26556             text: 'Underline the selected text.',
26557             cls: 'x-html-editor-tip'
26558         },
26559         increasefontsize : {
26560             title: 'Grow Text',
26561             text: 'Increase the font size.',
26562             cls: 'x-html-editor-tip'
26563         },
26564         decreasefontsize : {
26565             title: 'Shrink Text',
26566             text: 'Decrease the font size.',
26567             cls: 'x-html-editor-tip'
26568         },
26569         backcolor : {
26570             title: 'Text Highlight Color',
26571             text: 'Change the background color of the selected text.',
26572             cls: 'x-html-editor-tip'
26573         },
26574         forecolor : {
26575             title: 'Font Color',
26576             text: 'Change the color of the selected text.',
26577             cls: 'x-html-editor-tip'
26578         },
26579         justifyleft : {
26580             title: 'Align Text Left',
26581             text: 'Align text to the left.',
26582             cls: 'x-html-editor-tip'
26583         },
26584         justifycenter : {
26585             title: 'Center Text',
26586             text: 'Center text in the editor.',
26587             cls: 'x-html-editor-tip'
26588         },
26589         justifyright : {
26590             title: 'Align Text Right',
26591             text: 'Align text to the right.',
26592             cls: 'x-html-editor-tip'
26593         },
26594         insertunorderedlist : {
26595             title: 'Bullet List',
26596             text: 'Start a bulleted list.',
26597             cls: 'x-html-editor-tip'
26598         },
26599         insertorderedlist : {
26600             title: 'Numbered List',
26601             text: 'Start a numbered list.',
26602             cls: 'x-html-editor-tip'
26603         },
26604         createlink : {
26605             title: 'Hyperlink',
26606             text: 'Make the selected text a hyperlink.',
26607             cls: 'x-html-editor-tip'
26608         },
26609         sourceedit : {
26610             title: 'Source Edit',
26611             text: 'Switch to source editing mode.',
26612             cls: 'x-html-editor-tip'
26613         }
26614     },
26615     // private
26616     onDestroy : function(){
26617         if(this.rendered){
26618             
26619             this.tb.items.each(function(item){
26620                 if(item.menu){
26621                     item.menu.removeAll();
26622                     if(item.menu.el){
26623                         item.menu.el.destroy();
26624                     }
26625                 }
26626                 item.destroy();
26627             });
26628              
26629         }
26630     },
26631     onFirstFocus: function() {
26632         this.tb.items.each(function(item){
26633            item.enable();
26634         });
26635     }
26636 });
26637
26638
26639
26640
26641 // <script type="text/javascript">
26642 /*
26643  * Based on
26644  * Ext JS Library 1.1.1
26645  * Copyright(c) 2006-2007, Ext JS, LLC.
26646  *  
26647  
26648  */
26649
26650  
26651 /**
26652  * @class Roo.form.HtmlEditor.ToolbarContext
26653  * Context Toolbar
26654  * 
26655  * Usage:
26656  *
26657  new Roo.form.HtmlEditor({
26658     ....
26659     toolbars : [
26660         { xtype: 'ToolbarStandard', styles : {} }
26661         { xtype: 'ToolbarContext', disable : {} }
26662     ]
26663 })
26664
26665      
26666  * 
26667  * @config : {Object} disable List of elements to disable.. (not done yet.)
26668  * @config : {Object} styles  Map of styles available.
26669  * 
26670  */
26671
26672 Roo.form.HtmlEditor.ToolbarContext = function(config)
26673 {
26674     
26675     Roo.apply(this, config);
26676     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26677     // dont call parent... till later.
26678     this.styles = this.styles || {};
26679 }
26680 Roo.form.HtmlEditor.ToolbarContext.types = {
26681     'IMG' : {
26682         width : {
26683             title: "Width",
26684             width: 40
26685         },
26686         height:  {
26687             title: "Height",
26688             width: 40
26689         },
26690         align: {
26691             title: "Align",
26692             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
26693             width : 80
26694             
26695         },
26696         border: {
26697             title: "Border",
26698             width: 40
26699         },
26700         alt: {
26701             title: "Alt",
26702             width: 120
26703         },
26704         src : {
26705             title: "Src",
26706             width: 220
26707         }
26708         
26709     },
26710     'A' : {
26711         name : {
26712             title: "Name",
26713             width: 50
26714         },
26715         href:  {
26716             title: "Href",
26717             width: 220
26718         } // border?
26719         
26720     },
26721     'TABLE' : {
26722         rows : {
26723             title: "Rows",
26724             width: 20
26725         },
26726         cols : {
26727             title: "Cols",
26728             width: 20
26729         },
26730         width : {
26731             title: "Width",
26732             width: 40
26733         },
26734         height : {
26735             title: "Height",
26736             width: 40
26737         },
26738         border : {
26739             title: "Border",
26740             width: 20
26741         }
26742     },
26743     'TD' : {
26744         width : {
26745             title: "Width",
26746             width: 40
26747         },
26748         height : {
26749             title: "Height",
26750             width: 40
26751         },   
26752         align: {
26753             title: "Align",
26754             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
26755             width: 80
26756         },
26757         valign: {
26758             title: "Valign",
26759             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
26760             width: 80
26761         },
26762         colspan: {
26763             title: "Colspan",
26764             width: 20
26765             
26766         }
26767     },
26768     'INPUT' : {
26769         name : {
26770             title: "name",
26771             width: 120
26772         },
26773         value : {
26774             title: "Value",
26775             width: 120
26776         },
26777         width : {
26778             title: "Width",
26779             width: 40
26780         }
26781     },
26782     'LABEL' : {
26783         'for' : {
26784             title: "For",
26785             width: 120
26786         }
26787     },
26788     'TEXTAREA' : {
26789           name : {
26790             title: "name",
26791             width: 120
26792         },
26793         rows : {
26794             title: "Rows",
26795             width: 20
26796         },
26797         cols : {
26798             title: "Cols",
26799             width: 20
26800         }
26801     },
26802     'SELECT' : {
26803         name : {
26804             title: "name",
26805             width: 120
26806         },
26807         selectoptions : {
26808             title: "Options",
26809             width: 200
26810         }
26811     },
26812     
26813     // should we really allow this??
26814     // should this just be 
26815     'BODY' : {
26816         title : {
26817             title: "title",
26818             width: 200,
26819             disabled : true
26820         }
26821     },
26822     '*' : {
26823         // empty..
26824     }
26825 };
26826
26827
26828
26829 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
26830     
26831     tb: false,
26832     
26833     rendered: false,
26834     
26835     editor : false,
26836     /**
26837      * @cfg {Object} disable  List of toolbar elements to disable
26838          
26839      */
26840     disable : false,
26841     /**
26842      * @cfg {Object} styles List of styles 
26843      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
26844      *
26845      * These must be defined in the page, so they get rendered correctly..
26846      * .headline { }
26847      * TD.underline { }
26848      * 
26849      */
26850     styles : false,
26851     
26852     
26853     
26854     toolbars : false,
26855     
26856     init : function(editor)
26857     {
26858         this.editor = editor;
26859         
26860         
26861         var fid = editor.frameId;
26862         var etb = this;
26863         function btn(id, toggle, handler){
26864             var xid = fid + '-'+ id ;
26865             return {
26866                 id : xid,
26867                 cmd : id,
26868                 cls : 'x-btn-icon x-edit-'+id,
26869                 enableToggle:toggle !== false,
26870                 scope: editor, // was editor...
26871                 handler:handler||editor.relayBtnCmd,
26872                 clickEvent:'mousedown',
26873                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26874                 tabIndex:-1
26875             };
26876         }
26877         // create a new element.
26878         var wdiv = editor.wrap.createChild({
26879                 tag: 'div'
26880             }, editor.wrap.dom.firstChild.nextSibling, true);
26881         
26882         // can we do this more than once??
26883         
26884          // stop form submits
26885       
26886  
26887         // disable everything...
26888         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26889         this.toolbars = {};
26890            
26891         for (var i in  ty) {
26892           
26893             this.toolbars[i] = this.buildToolbar(ty[i],i);
26894         }
26895         this.tb = this.toolbars.BODY;
26896         this.tb.el.show();
26897         this.buildFooter();
26898         this.footer.show();
26899         editor.on('hide', function( ) { this.footer.hide() }, this);
26900         editor.on('show', function( ) { this.footer.show() }, this);
26901         
26902          
26903         this.rendered = true;
26904         
26905         // the all the btns;
26906         editor.on('editorevent', this.updateToolbar, this);
26907         // other toolbars need to implement this..
26908         //editor.on('editmodechange', this.updateToolbar, this);
26909     },
26910     
26911     
26912     
26913     /**
26914      * Protected method that will not generally be called directly. It triggers
26915      * a toolbar update by reading the markup state of the current selection in the editor.
26916      */
26917     updateToolbar: function(editor,ev,sel){
26918
26919         //Roo.log(ev);
26920         // capture mouse up - this is handy for selecting images..
26921         // perhaps should go somewhere else...
26922         if(!this.editor.activated){
26923              this.editor.onFirstFocus();
26924             return;
26925         }
26926         
26927         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
26928         // selectNode - might want to handle IE?
26929         if (ev &&
26930             (ev.type == 'mouseup' || ev.type == 'click' ) &&
26931             ev.target && ev.target.tagName == 'IMG') {
26932             // they have click on an image...
26933             // let's see if we can change the selection...
26934             sel = ev.target;
26935          
26936               var nodeRange = sel.ownerDocument.createRange();
26937             try {
26938                 nodeRange.selectNode(sel);
26939             } catch (e) {
26940                 nodeRange.selectNodeContents(sel);
26941             }
26942             //nodeRange.collapse(true);
26943             var s = editor.win.getSelection();
26944             s.removeAllRanges();
26945             s.addRange(nodeRange);
26946         }  
26947         
26948       
26949         var updateFooter = sel ? false : true;
26950         
26951         
26952         var ans = this.editor.getAllAncestors();
26953         
26954         // pick
26955         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26956         
26957         if (!sel) { 
26958             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
26959             sel = sel ? sel : this.editor.doc.body;
26960             sel = sel.tagName.length ? sel : this.editor.doc.body;
26961             
26962         }
26963         // pick a menu that exists..
26964         var tn = sel.tagName.toUpperCase();
26965         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
26966         
26967         tn = sel.tagName.toUpperCase();
26968         
26969         var lastSel = this.tb.selectedNode
26970         
26971         this.tb.selectedNode = sel;
26972         
26973         // if current menu does not match..
26974         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
26975                 
26976             this.tb.el.hide();
26977             ///console.log("show: " + tn);
26978             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
26979             this.tb.el.show();
26980             // update name
26981             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
26982             
26983             
26984             // update attributes
26985             if (this.tb.fields) {
26986                 this.tb.fields.each(function(e) {
26987                    e.setValue(sel.getAttribute(e.attrname));
26988                 });
26989             }
26990             
26991             var hasStyles = false;
26992             for(var i in this.styles) {
26993                 hasStyles = true;
26994                 break;
26995             }
26996             
26997             // update styles
26998             if (hasStyles) { 
26999                 var st = this.tb.fields.item(0);
27000                 
27001                 st.store.removeAll();
27002                
27003                 
27004                 var cn = sel.className.split(/\s+/);
27005                 
27006                 var avs = [];
27007                 if (this.styles['*']) {
27008                     
27009                     Roo.each(this.styles['*'], function(v) {
27010                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27011                     });
27012                 }
27013                 if (this.styles[tn]) { 
27014                     Roo.each(this.styles[tn], function(v) {
27015                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27016                     });
27017                 }
27018                 
27019                 st.store.loadData(avs);
27020                 st.collapse();
27021                 st.setValue(cn);
27022             }
27023             // flag our selected Node.
27024             this.tb.selectedNode = sel;
27025            
27026            
27027             Roo.menu.MenuMgr.hideAll();
27028
27029         }
27030         
27031         if (!updateFooter) {
27032             return;
27033         }
27034         // update the footer
27035         //
27036         var html = '';
27037         
27038         this.footerEls = ans.reverse();
27039         Roo.each(this.footerEls, function(a,i) {
27040             if (!a) { return; }
27041             html += html.length ? ' &gt; '  :  '';
27042             
27043             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27044             
27045         });
27046        
27047         // 
27048         var sz = this.footDisp.up('td').getSize();
27049         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27050         this.footDisp.dom.style.marginLeft = '5px';
27051         
27052         this.footDisp.dom.style.overflow = 'hidden';
27053         
27054         this.footDisp.dom.innerHTML = html;
27055             
27056         //this.editorsyncValue();
27057     },
27058    
27059        
27060     // private
27061     onDestroy : function(){
27062         if(this.rendered){
27063             
27064             this.tb.items.each(function(item){
27065                 if(item.menu){
27066                     item.menu.removeAll();
27067                     if(item.menu.el){
27068                         item.menu.el.destroy();
27069                     }
27070                 }
27071                 item.destroy();
27072             });
27073              
27074         }
27075     },
27076     onFirstFocus: function() {
27077         // need to do this for all the toolbars..
27078         this.tb.items.each(function(item){
27079            item.enable();
27080         });
27081     },
27082     buildToolbar: function(tlist, nm)
27083     {
27084         var editor = this.editor;
27085          // create a new element.
27086         var wdiv = editor.wrap.createChild({
27087                 tag: 'div'
27088             }, editor.wrap.dom.firstChild.nextSibling, true);
27089         
27090        
27091         var tb = new Roo.Toolbar(wdiv);
27092         // add the name..
27093         
27094         tb.add(nm+ ":&nbsp;");
27095         
27096         var styles = [];
27097         for(var i in this.styles) {
27098             styles.push(i);
27099         }
27100         
27101         // styles...
27102         if (styles && styles.length) {
27103             
27104             // this needs a multi-select checkbox...
27105             tb.addField( new Roo.form.ComboBox({
27106                 store: new Roo.data.SimpleStore({
27107                     id : 'val',
27108                     fields: ['val', 'selected'],
27109                     data : [] 
27110                 }),
27111                 name : '-roo-edit-className',
27112                 attrname : 'className',
27113                 displayField:'val',
27114                 typeAhead: false,
27115                 mode: 'local',
27116                 editable : false,
27117                 triggerAction: 'all',
27118                 emptyText:'Select Style',
27119                 selectOnFocus:true,
27120                 width: 130,
27121                 listeners : {
27122                     'select': function(c, r, i) {
27123                         // initial support only for on class per el..
27124                         tb.selectedNode.className =  r ? r.get('val') : '';
27125                         editor.syncValue();
27126                     }
27127                 }
27128     
27129             }));
27130         }
27131             
27132         
27133         
27134         for (var i in tlist) {
27135             
27136             var item = tlist[i];
27137             tb.add(item.title + ":&nbsp;");
27138             
27139             
27140             
27141             
27142             if (item.opts) {
27143                 // opts == pulldown..
27144                 tb.addField( new Roo.form.ComboBox({
27145                     store: new Roo.data.SimpleStore({
27146                         id : 'val',
27147                         fields: ['val'],
27148                         data : item.opts  
27149                     }),
27150                     name : '-roo-edit-' + i,
27151                     attrname : i,
27152                     displayField:'val',
27153                     typeAhead: false,
27154                     mode: 'local',
27155                     editable : false,
27156                     triggerAction: 'all',
27157                     emptyText:'Select',
27158                     selectOnFocus:true,
27159                     width: item.width ? item.width  : 130,
27160                     listeners : {
27161                         'select': function(c, r, i) {
27162                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27163                         }
27164                     }
27165
27166                 }));
27167                 continue;
27168                     
27169                  
27170                 
27171                 tb.addField( new Roo.form.TextField({
27172                     name: i,
27173                     width: 100,
27174                     //allowBlank:false,
27175                     value: ''
27176                 }));
27177                 continue;
27178             }
27179             tb.addField( new Roo.form.TextField({
27180                 name: '-roo-edit-' + i,
27181                 attrname : i,
27182                 
27183                 width: item.width,
27184                 //allowBlank:true,
27185                 value: '',
27186                 listeners: {
27187                     'change' : function(f, nv, ov) {
27188                         tb.selectedNode.setAttribute(f.attrname, nv);
27189                     }
27190                 }
27191             }));
27192              
27193         }
27194         tb.el.on('click', function(e){
27195             e.preventDefault(); // what does this do?
27196         });
27197         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27198         tb.el.hide();
27199         tb.name = nm;
27200         // dont need to disable them... as they will get hidden
27201         return tb;
27202          
27203         
27204     },
27205     buildFooter : function()
27206     {
27207         
27208         var fel = this.editor.wrap.createChild();
27209         this.footer = new Roo.Toolbar(fel);
27210         // toolbar has scrolly on left / right?
27211         var footDisp= new Roo.Toolbar.Fill();
27212         var _t = this;
27213         this.footer.add(
27214             {
27215                 text : '&lt;',
27216                 xtype: 'Button',
27217                 handler : function() {
27218                     _t.footDisp.scrollTo('left',0,true)
27219                 }
27220             }
27221         );
27222         this.footer.add( footDisp );
27223         this.footer.add( 
27224             {
27225                 text : '&gt;',
27226                 xtype: 'Button',
27227                 handler : function() {
27228                     // no animation..
27229                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27230                 }
27231             }
27232         );
27233         var fel = Roo.get(footDisp.el);
27234         fel.addClass('x-editor-context');
27235         this.footDispWrap = fel; 
27236         this.footDispWrap.overflow  = 'hidden';
27237         
27238         this.footDisp = fel.createChild();
27239         this.footDispWrap.on('click', this.onContextClick, this)
27240         
27241         
27242     },
27243     onContextClick : function (ev,dom)
27244     {
27245         ev.preventDefault();
27246         var  cn = dom.className;
27247         Roo.log(cn);
27248         if (!cn.match(/x-ed-loc-/)) {
27249             return;
27250         }
27251         var n = cn.split('-').pop();
27252         var ans = this.footerEls;
27253         var sel = ans[n];
27254         
27255          // pick
27256         var range = this.editor.createRange();
27257         
27258         range.selectNodeContents(sel);
27259         //range.selectNode(sel);
27260         
27261         
27262         var selection = this.editor.getSelection();
27263         selection.removeAllRanges();
27264         selection.addRange(range);
27265         
27266         
27267         
27268         this.updateToolbar(null, null, sel);
27269         
27270         
27271     }
27272     
27273     
27274     
27275     
27276     
27277 });
27278
27279
27280
27281
27282
27283 /*
27284  * Based on:
27285  * Ext JS Library 1.1.1
27286  * Copyright(c) 2006-2007, Ext JS, LLC.
27287  *
27288  * Originally Released Under LGPL - original licence link has changed is not relivant.
27289  *
27290  * Fork - LGPL
27291  * <script type="text/javascript">
27292  */
27293  
27294 /**
27295  * @class Roo.form.BasicForm
27296  * @extends Roo.util.Observable
27297  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
27298  * @constructor
27299  * @param {String/HTMLElement/Roo.Element} el The form element or its id
27300  * @param {Object} config Configuration options
27301  */
27302 Roo.form.BasicForm = function(el, config){
27303     this.allItems = [];
27304     this.childForms = [];
27305     Roo.apply(this, config);
27306     /*
27307      * The Roo.form.Field items in this form.
27308      * @type MixedCollection
27309      */
27310      
27311      
27312     this.items = new Roo.util.MixedCollection(false, function(o){
27313         return o.id || (o.id = Roo.id());
27314     });
27315     this.addEvents({
27316         /**
27317          * @event beforeaction
27318          * Fires before any action is performed. Return false to cancel the action.
27319          * @param {Form} this
27320          * @param {Action} action The action to be performed
27321          */
27322         beforeaction: true,
27323         /**
27324          * @event actionfailed
27325          * Fires when an action fails.
27326          * @param {Form} this
27327          * @param {Action} action The action that failed
27328          */
27329         actionfailed : true,
27330         /**
27331          * @event actioncomplete
27332          * Fires when an action is completed.
27333          * @param {Form} this
27334          * @param {Action} action The action that completed
27335          */
27336         actioncomplete : true
27337     });
27338     if(el){
27339         this.initEl(el);
27340     }
27341     Roo.form.BasicForm.superclass.constructor.call(this);
27342 };
27343
27344 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
27345     /**
27346      * @cfg {String} method
27347      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
27348      */
27349     /**
27350      * @cfg {DataReader} reader
27351      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
27352      * This is optional as there is built-in support for processing JSON.
27353      */
27354     /**
27355      * @cfg {DataReader} errorReader
27356      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
27357      * This is completely optional as there is built-in support for processing JSON.
27358      */
27359     /**
27360      * @cfg {String} url
27361      * The URL to use for form actions if one isn't supplied in the action options.
27362      */
27363     /**
27364      * @cfg {Boolean} fileUpload
27365      * Set to true if this form is a file upload.
27366      */
27367      
27368     /**
27369      * @cfg {Object} baseParams
27370      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
27371      */
27372      /**
27373      
27374     /**
27375      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
27376      */
27377     timeout: 30,
27378
27379     // private
27380     activeAction : null,
27381
27382     /**
27383      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
27384      * or setValues() data instead of when the form was first created.
27385      */
27386     trackResetOnLoad : false,
27387     
27388     
27389     /**
27390      * childForms - used for multi-tab forms
27391      * @type {Array}
27392      */
27393     childForms : false,
27394     
27395     /**
27396      * allItems - full list of fields.
27397      * @type {Array}
27398      */
27399     allItems : false,
27400     
27401     /**
27402      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
27403      * element by passing it or its id or mask the form itself by passing in true.
27404      * @type Mixed
27405      */
27406     waitMsgTarget : false,
27407
27408     // private
27409     initEl : function(el){
27410         this.el = Roo.get(el);
27411         this.id = this.el.id || Roo.id();
27412         this.el.on('submit', this.onSubmit, this);
27413         this.el.addClass('x-form');
27414     },
27415
27416     // private
27417     onSubmit : function(e){
27418         e.stopEvent();
27419     },
27420
27421     /**
27422      * Returns true if client-side validation on the form is successful.
27423      * @return Boolean
27424      */
27425     isValid : function(){
27426         var valid = true;
27427         this.items.each(function(f){
27428            if(!f.validate()){
27429                valid = false;
27430            }
27431         });
27432         return valid;
27433     },
27434
27435     /**
27436      * Returns true if any fields in this form have changed since their original load.
27437      * @return Boolean
27438      */
27439     isDirty : function(){
27440         var dirty = false;
27441         this.items.each(function(f){
27442            if(f.isDirty()){
27443                dirty = true;
27444                return false;
27445            }
27446         });
27447         return dirty;
27448     },
27449
27450     /**
27451      * Performs a predefined action (submit or load) or custom actions you define on this form.
27452      * @param {String} actionName The name of the action type
27453      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
27454      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
27455      * accept other config options):
27456      * <pre>
27457 Property          Type             Description
27458 ----------------  ---------------  ----------------------------------------------------------------------------------
27459 url               String           The url for the action (defaults to the form's url)
27460 method            String           The form method to use (defaults to the form's method, or POST if not defined)
27461 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
27462 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
27463                                    validate the form on the client (defaults to false)
27464      * </pre>
27465      * @return {BasicForm} this
27466      */
27467     doAction : function(action, options){
27468         if(typeof action == 'string'){
27469             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
27470         }
27471         if(this.fireEvent('beforeaction', this, action) !== false){
27472             this.beforeAction(action);
27473             action.run.defer(100, action);
27474         }
27475         return this;
27476     },
27477
27478     /**
27479      * Shortcut to do a submit action.
27480      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27481      * @return {BasicForm} this
27482      */
27483     submit : function(options){
27484         this.doAction('submit', options);
27485         return this;
27486     },
27487
27488     /**
27489      * Shortcut to do a load action.
27490      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27491      * @return {BasicForm} this
27492      */
27493     load : function(options){
27494         this.doAction('load', options);
27495         return this;
27496     },
27497
27498     /**
27499      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
27500      * @param {Record} record The record to edit
27501      * @return {BasicForm} this
27502      */
27503     updateRecord : function(record){
27504         record.beginEdit();
27505         var fs = record.fields;
27506         fs.each(function(f){
27507             var field = this.findField(f.name);
27508             if(field){
27509                 record.set(f.name, field.getValue());
27510             }
27511         }, this);
27512         record.endEdit();
27513         return this;
27514     },
27515
27516     /**
27517      * Loads an Roo.data.Record into this form.
27518      * @param {Record} record The record to load
27519      * @return {BasicForm} this
27520      */
27521     loadRecord : function(record){
27522         this.setValues(record.data);
27523         return this;
27524     },
27525
27526     // private
27527     beforeAction : function(action){
27528         var o = action.options;
27529         
27530        
27531         if(this.waitMsgTarget === true){
27532             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
27533         }else if(this.waitMsgTarget){
27534             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
27535             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
27536         }else {
27537             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
27538         }
27539          
27540     },
27541
27542     // private
27543     afterAction : function(action, success){
27544         this.activeAction = null;
27545         var o = action.options;
27546         
27547         if(this.waitMsgTarget === true){
27548             this.el.unmask();
27549         }else if(this.waitMsgTarget){
27550             this.waitMsgTarget.unmask();
27551         }else{
27552             Roo.MessageBox.updateProgress(1);
27553             Roo.MessageBox.hide();
27554         }
27555          
27556         if(success){
27557             if(o.reset){
27558                 this.reset();
27559             }
27560             Roo.callback(o.success, o.scope, [this, action]);
27561             this.fireEvent('actioncomplete', this, action);
27562             
27563         }else{
27564             
27565             // failure condition..
27566             // we have a scenario where updates need confirming.
27567             // eg. if a locking scenario exists..
27568             // we look for { errors : { needs_confirm : true }} in the response.
27569             if (
27570                 (typeof(action.result) != 'undefined')  &&
27571                 (typeof(action.result.errors) != 'undefined')  &&
27572                 (typeof(action.result.errors.needs_confirm) != 'undefined')
27573           ){
27574                 var _t = this;
27575                 Roo.MessageBox.confirm(
27576                     "Change requires confirmation",
27577                     action.result.errorMsg,
27578                     function(r) {
27579                         if (r != 'yes') {
27580                             return;
27581                         }
27582                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
27583                     }
27584                     
27585                 );
27586                 
27587                 
27588                 
27589                 return;
27590             }
27591             
27592             Roo.callback(o.failure, o.scope, [this, action]);
27593             // show an error message if no failed handler is set..
27594             if (!this.hasListener('actionfailed')) {
27595                 Roo.MessageBox.alert("Error",
27596                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
27597                         action.result.errorMsg :
27598                         "Saving Failed, please check your entries or try again"
27599                 );
27600             }
27601             
27602             this.fireEvent('actionfailed', this, action);
27603         }
27604         
27605     },
27606
27607     /**
27608      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
27609      * @param {String} id The value to search for
27610      * @return Field
27611      */
27612     findField : function(id){
27613         var field = this.items.get(id);
27614         if(!field){
27615             this.items.each(function(f){
27616                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
27617                     field = f;
27618                     return false;
27619                 }
27620             });
27621         }
27622         return field || null;
27623     },
27624
27625     /**
27626      * Add a secondary form to this one, 
27627      * Used to provide tabbed forms. One form is primary, with hidden values 
27628      * which mirror the elements from the other forms.
27629      * 
27630      * @param {Roo.form.Form} form to add.
27631      * 
27632      */
27633     addForm : function(form)
27634     {
27635        
27636         if (this.childForms.indexOf(form) > -1) {
27637             // already added..
27638             return;
27639         }
27640         this.childForms.push(form);
27641         var n = '';
27642         Roo.each(form.allItems, function (fe) {
27643             
27644             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
27645             if (this.findField(n)) { // already added..
27646                 return;
27647             }
27648             var add = new Roo.form.Hidden({
27649                 name : n
27650             });
27651             add.render(this.el);
27652             
27653             this.add( add );
27654         }, this);
27655         
27656     },
27657     /**
27658      * Mark fields in this form invalid in bulk.
27659      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
27660      * @return {BasicForm} this
27661      */
27662     markInvalid : function(errors){
27663         if(errors instanceof Array){
27664             for(var i = 0, len = errors.length; i < len; i++){
27665                 var fieldError = errors[i];
27666                 var f = this.findField(fieldError.id);
27667                 if(f){
27668                     f.markInvalid(fieldError.msg);
27669                 }
27670             }
27671         }else{
27672             var field, id;
27673             for(id in errors){
27674                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
27675                     field.markInvalid(errors[id]);
27676                 }
27677             }
27678         }
27679         Roo.each(this.childForms || [], function (f) {
27680             f.markInvalid(errors);
27681         });
27682         
27683         return this;
27684     },
27685
27686     /**
27687      * Set values for fields in this form in bulk.
27688      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
27689      * @return {BasicForm} this
27690      */
27691     setValues : function(values){
27692         if(values instanceof Array){ // array of objects
27693             for(var i = 0, len = values.length; i < len; i++){
27694                 var v = values[i];
27695                 var f = this.findField(v.id);
27696                 if(f){
27697                     f.setValue(v.value);
27698                     if(this.trackResetOnLoad){
27699                         f.originalValue = f.getValue();
27700                     }
27701                 }
27702             }
27703         }else{ // object hash
27704             var field, id;
27705             for(id in values){
27706                 if(typeof values[id] != 'function' && (field = this.findField(id))){
27707                     
27708                     if (field.setFromData && 
27709                         field.valueField && 
27710                         field.displayField &&
27711                         // combos' with local stores can 
27712                         // be queried via setValue()
27713                         // to set their value..
27714                         (field.store && !field.store.isLocal)
27715                         ) {
27716                         // it's a combo
27717                         var sd = { };
27718                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
27719                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
27720                         field.setFromData(sd);
27721                         
27722                     } else {
27723                         field.setValue(values[id]);
27724                     }
27725                     
27726                     
27727                     if(this.trackResetOnLoad){
27728                         field.originalValue = field.getValue();
27729                     }
27730                 }
27731             }
27732         }
27733          
27734         Roo.each(this.childForms || [], function (f) {
27735             f.setValues(values);
27736         });
27737                 
27738         return this;
27739     },
27740
27741     /**
27742      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
27743      * they are returned as an array.
27744      * @param {Boolean} asString
27745      * @return {Object}
27746      */
27747     getValues : function(asString){
27748         if (this.childForms) {
27749             // copy values from the child forms
27750             Roo.each(this.childForms, function (f) {
27751                 this.setValues(f.getValues());
27752             }, this);
27753         }
27754         
27755         
27756         
27757         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
27758         if(asString === true){
27759             return fs;
27760         }
27761         return Roo.urlDecode(fs);
27762     },
27763     
27764     /**
27765      * Returns the fields in this form as an object with key/value pairs. 
27766      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
27767      * @return {Object}
27768      */
27769     getFieldValues : function(with_hidden)
27770     {
27771         if (this.childForms) {
27772             // copy values from the child forms
27773             // should this call getFieldValues - probably not as we do not currently copy
27774             // hidden fields when we generate..
27775             Roo.each(this.childForms, function (f) {
27776                 this.setValues(f.getValues());
27777             }, this);
27778         }
27779         
27780         var ret = {};
27781         this.items.each(function(f){
27782             if (!f.getName()) {
27783                 return;
27784             }
27785             var v = f.getValue();
27786             // not sure if this supported any more..
27787             if ((typeof(v) == 'object') && f.getRawValue) {
27788                 v = f.getRawValue() ; // dates..
27789             }
27790             // combo boxes where name != hiddenName...
27791             if (f.name != f.getName()) {
27792                 ret[f.name] = f.getRawValue();
27793             }
27794             ret[f.getName()] = v;
27795         });
27796         
27797         return ret;
27798     },
27799
27800     /**
27801      * Clears all invalid messages in this form.
27802      * @return {BasicForm} this
27803      */
27804     clearInvalid : function(){
27805         this.items.each(function(f){
27806            f.clearInvalid();
27807         });
27808         
27809         Roo.each(this.childForms || [], function (f) {
27810             f.clearInvalid();
27811         });
27812         
27813         
27814         return this;
27815     },
27816
27817     /**
27818      * Resets this form.
27819      * @return {BasicForm} this
27820      */
27821     reset : function(){
27822         this.items.each(function(f){
27823             f.reset();
27824         });
27825         
27826         Roo.each(this.childForms || [], function (f) {
27827             f.reset();
27828         });
27829        
27830         
27831         return this;
27832     },
27833
27834     /**
27835      * Add Roo.form components to this form.
27836      * @param {Field} field1
27837      * @param {Field} field2 (optional)
27838      * @param {Field} etc (optional)
27839      * @return {BasicForm} this
27840      */
27841     add : function(){
27842         this.items.addAll(Array.prototype.slice.call(arguments, 0));
27843         return this;
27844     },
27845
27846
27847     /**
27848      * Removes a field from the items collection (does NOT remove its markup).
27849      * @param {Field} field
27850      * @return {BasicForm} this
27851      */
27852     remove : function(field){
27853         this.items.remove(field);
27854         return this;
27855     },
27856
27857     /**
27858      * Looks at the fields in this form, checks them for an id attribute,
27859      * and calls applyTo on the existing dom element with that id.
27860      * @return {BasicForm} this
27861      */
27862     render : function(){
27863         this.items.each(function(f){
27864             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
27865                 f.applyTo(f.id);
27866             }
27867         });
27868         return this;
27869     },
27870
27871     /**
27872      * Calls {@link Ext#apply} for all fields in this form with the passed object.
27873      * @param {Object} values
27874      * @return {BasicForm} this
27875      */
27876     applyToFields : function(o){
27877         this.items.each(function(f){
27878            Roo.apply(f, o);
27879         });
27880         return this;
27881     },
27882
27883     /**
27884      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
27885      * @param {Object} values
27886      * @return {BasicForm} this
27887      */
27888     applyIfToFields : function(o){
27889         this.items.each(function(f){
27890            Roo.applyIf(f, o);
27891         });
27892         return this;
27893     }
27894 });
27895
27896 // back compat
27897 Roo.BasicForm = Roo.form.BasicForm;/*
27898  * Based on:
27899  * Ext JS Library 1.1.1
27900  * Copyright(c) 2006-2007, Ext JS, LLC.
27901  *
27902  * Originally Released Under LGPL - original licence link has changed is not relivant.
27903  *
27904  * Fork - LGPL
27905  * <script type="text/javascript">
27906  */
27907
27908 /**
27909  * @class Roo.form.Form
27910  * @extends Roo.form.BasicForm
27911  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
27912  * @constructor
27913  * @param {Object} config Configuration options
27914  */
27915 Roo.form.Form = function(config){
27916     var xitems =  [];
27917     if (config.items) {
27918         xitems = config.items;
27919         delete config.items;
27920     }
27921    
27922     
27923     Roo.form.Form.superclass.constructor.call(this, null, config);
27924     this.url = this.url || this.action;
27925     if(!this.root){
27926         this.root = new Roo.form.Layout(Roo.applyIf({
27927             id: Roo.id()
27928         }, config));
27929     }
27930     this.active = this.root;
27931     /**
27932      * Array of all the buttons that have been added to this form via {@link addButton}
27933      * @type Array
27934      */
27935     this.buttons = [];
27936     this.allItems = [];
27937     this.addEvents({
27938         /**
27939          * @event clientvalidation
27940          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
27941          * @param {Form} this
27942          * @param {Boolean} valid true if the form has passed client-side validation
27943          */
27944         clientvalidation: true,
27945         /**
27946          * @event rendered
27947          * Fires when the form is rendered
27948          * @param {Roo.form.Form} form
27949          */
27950         rendered : true
27951     });
27952     
27953     if (this.progressUrl) {
27954             // push a hidden field onto the list of fields..
27955             this.addxtype( {
27956                     xns: Roo.form, 
27957                     xtype : 'Hidden', 
27958                     name : 'UPLOAD_IDENTIFIER' 
27959             });
27960         }
27961         
27962     
27963     Roo.each(xitems, this.addxtype, this);
27964     
27965     
27966     
27967 };
27968
27969 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
27970     /**
27971      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
27972      */
27973     /**
27974      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
27975      */
27976     /**
27977      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
27978      */
27979     buttonAlign:'center',
27980
27981     /**
27982      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
27983      */
27984     minButtonWidth:75,
27985
27986     /**
27987      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
27988      * This property cascades to child containers if not set.
27989      */
27990     labelAlign:'left',
27991
27992     /**
27993      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
27994      * fires a looping event with that state. This is required to bind buttons to the valid
27995      * state using the config value formBind:true on the button.
27996      */
27997     monitorValid : false,
27998
27999     /**
28000      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28001      */
28002     monitorPoll : 200,
28003     
28004     /**
28005      * @cfg {String} progressUrl - Url to return progress data 
28006      */
28007     
28008     progressUrl : false,
28009   
28010     /**
28011      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28012      * fields are added and the column is closed. If no fields are passed the column remains open
28013      * until end() is called.
28014      * @param {Object} config The config to pass to the column
28015      * @param {Field} field1 (optional)
28016      * @param {Field} field2 (optional)
28017      * @param {Field} etc (optional)
28018      * @return Column The column container object
28019      */
28020     column : function(c){
28021         var col = new Roo.form.Column(c);
28022         this.start(col);
28023         if(arguments.length > 1){ // duplicate code required because of Opera
28024             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28025             this.end();
28026         }
28027         return col;
28028     },
28029
28030     /**
28031      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28032      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28033      * until end() is called.
28034      * @param {Object} config The config to pass to the fieldset
28035      * @param {Field} field1 (optional)
28036      * @param {Field} field2 (optional)
28037      * @param {Field} etc (optional)
28038      * @return FieldSet The fieldset container object
28039      */
28040     fieldset : function(c){
28041         var fs = new Roo.form.FieldSet(c);
28042         this.start(fs);
28043         if(arguments.length > 1){ // duplicate code required because of Opera
28044             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28045             this.end();
28046         }
28047         return fs;
28048     },
28049
28050     /**
28051      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28052      * fields are added and the container is closed. If no fields are passed the container remains open
28053      * until end() is called.
28054      * @param {Object} config The config to pass to the Layout
28055      * @param {Field} field1 (optional)
28056      * @param {Field} field2 (optional)
28057      * @param {Field} etc (optional)
28058      * @return Layout The container object
28059      */
28060     container : function(c){
28061         var l = new Roo.form.Layout(c);
28062         this.start(l);
28063         if(arguments.length > 1){ // duplicate code required because of Opera
28064             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28065             this.end();
28066         }
28067         return l;
28068     },
28069
28070     /**
28071      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28072      * @param {Object} container A Roo.form.Layout or subclass of Layout
28073      * @return {Form} this
28074      */
28075     start : function(c){
28076         // cascade label info
28077         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28078         this.active.stack.push(c);
28079         c.ownerCt = this.active;
28080         this.active = c;
28081         return this;
28082     },
28083
28084     /**
28085      * Closes the current open container
28086      * @return {Form} this
28087      */
28088     end : function(){
28089         if(this.active == this.root){
28090             return this;
28091         }
28092         this.active = this.active.ownerCt;
28093         return this;
28094     },
28095
28096     /**
28097      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28098      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28099      * as the label of the field.
28100      * @param {Field} field1
28101      * @param {Field} field2 (optional)
28102      * @param {Field} etc. (optional)
28103      * @return {Form} this
28104      */
28105     add : function(){
28106         this.active.stack.push.apply(this.active.stack, arguments);
28107         this.allItems.push.apply(this.allItems,arguments);
28108         var r = [];
28109         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28110             if(a[i].isFormField){
28111                 r.push(a[i]);
28112             }
28113         }
28114         if(r.length > 0){
28115             Roo.form.Form.superclass.add.apply(this, r);
28116         }
28117         return this;
28118     },
28119     
28120
28121     
28122     
28123     
28124      /**
28125      * Find any element that has been added to a form, using it's ID or name
28126      * This can include framesets, columns etc. along with regular fields..
28127      * @param {String} id - id or name to find.
28128      
28129      * @return {Element} e - or false if nothing found.
28130      */
28131     findbyId : function(id)
28132     {
28133         var ret = false;
28134         if (!id) {
28135             return ret;
28136         }
28137         Roo.each(this.allItems, function(f){
28138             if (f.id == id || f.name == id ){
28139                 ret = f;
28140                 return false;
28141             }
28142         });
28143         return ret;
28144     },
28145
28146     
28147     
28148     /**
28149      * Render this form into the passed container. This should only be called once!
28150      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28151      * @return {Form} this
28152      */
28153     render : function(ct)
28154     {
28155         
28156         
28157         
28158         ct = Roo.get(ct);
28159         var o = this.autoCreate || {
28160             tag: 'form',
28161             method : this.method || 'POST',
28162             id : this.id || Roo.id()
28163         };
28164         this.initEl(ct.createChild(o));
28165
28166         this.root.render(this.el);
28167         
28168        
28169              
28170         this.items.each(function(f){
28171             f.render('x-form-el-'+f.id);
28172         });
28173
28174         if(this.buttons.length > 0){
28175             // tables are required to maintain order and for correct IE layout
28176             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28177                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28178                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28179             }}, null, true);
28180             var tr = tb.getElementsByTagName('tr')[0];
28181             for(var i = 0, len = this.buttons.length; i < len; i++) {
28182                 var b = this.buttons[i];
28183                 var td = document.createElement('td');
28184                 td.className = 'x-form-btn-td';
28185                 b.render(tr.appendChild(td));
28186             }
28187         }
28188         if(this.monitorValid){ // initialize after render
28189             this.startMonitoring();
28190         }
28191         this.fireEvent('rendered', this);
28192         return this;
28193     },
28194
28195     /**
28196      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28197      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28198      * object or a valid Roo.DomHelper element config
28199      * @param {Function} handler The function called when the button is clicked
28200      * @param {Object} scope (optional) The scope of the handler function
28201      * @return {Roo.Button}
28202      */
28203     addButton : function(config, handler, scope){
28204         var bc = {
28205             handler: handler,
28206             scope: scope,
28207             minWidth: this.minButtonWidth,
28208             hideParent:true
28209         };
28210         if(typeof config == "string"){
28211             bc.text = config;
28212         }else{
28213             Roo.apply(bc, config);
28214         }
28215         var btn = new Roo.Button(null, bc);
28216         this.buttons.push(btn);
28217         return btn;
28218     },
28219
28220      /**
28221      * Adds a series of form elements (using the xtype property as the factory method.
28222      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28223      * @param {Object} config 
28224      */
28225     
28226     addxtype : function()
28227     {
28228         var ar = Array.prototype.slice.call(arguments, 0);
28229         var ret = false;
28230         for(var i = 0; i < ar.length; i++) {
28231             if (!ar[i]) {
28232                 continue; // skip -- if this happends something invalid got sent, we 
28233                 // should ignore it, as basically that interface element will not show up
28234                 // and that should be pretty obvious!!
28235             }
28236             
28237             if (Roo.form[ar[i].xtype]) {
28238                 ar[i].form = this;
28239                 var fe = Roo.factory(ar[i], Roo.form);
28240                 if (!ret) {
28241                     ret = fe;
28242                 }
28243                 fe.form = this;
28244                 if (fe.store) {
28245                     fe.store.form = this;
28246                 }
28247                 if (fe.isLayout) {  
28248                          
28249                     this.start(fe);
28250                     this.allItems.push(fe);
28251                     if (fe.items && fe.addxtype) {
28252                         fe.addxtype.apply(fe, fe.items);
28253                         delete fe.items;
28254                     }
28255                      this.end();
28256                     continue;
28257                 }
28258                 
28259                 
28260                  
28261                 this.add(fe);
28262               //  console.log('adding ' + ar[i].xtype);
28263             }
28264             if (ar[i].xtype == 'Button') {  
28265                 //console.log('adding button');
28266                 //console.log(ar[i]);
28267                 this.addButton(ar[i]);
28268                 this.allItems.push(fe);
28269                 continue;
28270             }
28271             
28272             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28273                 alert('end is not supported on xtype any more, use items');
28274             //    this.end();
28275             //    //console.log('adding end');
28276             }
28277             
28278         }
28279         return ret;
28280     },
28281     
28282     /**
28283      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
28284      * option "monitorValid"
28285      */
28286     startMonitoring : function(){
28287         if(!this.bound){
28288             this.bound = true;
28289             Roo.TaskMgr.start({
28290                 run : this.bindHandler,
28291                 interval : this.monitorPoll || 200,
28292                 scope: this
28293             });
28294         }
28295     },
28296
28297     /**
28298      * Stops monitoring of the valid state of this form
28299      */
28300     stopMonitoring : function(){
28301         this.bound = false;
28302     },
28303
28304     // private
28305     bindHandler : function(){
28306         if(!this.bound){
28307             return false; // stops binding
28308         }
28309         var valid = true;
28310         this.items.each(function(f){
28311             if(!f.isValid(true)){
28312                 valid = false;
28313                 return false;
28314             }
28315         });
28316         for(var i = 0, len = this.buttons.length; i < len; i++){
28317             var btn = this.buttons[i];
28318             if(btn.formBind === true && btn.disabled === valid){
28319                 btn.setDisabled(!valid);
28320             }
28321         }
28322         this.fireEvent('clientvalidation', this, valid);
28323     }
28324     
28325     
28326     
28327     
28328     
28329     
28330     
28331     
28332 });
28333
28334
28335 // back compat
28336 Roo.Form = Roo.form.Form;
28337 /*
28338  * Based on:
28339  * Ext JS Library 1.1.1
28340  * Copyright(c) 2006-2007, Ext JS, LLC.
28341  *
28342  * Originally Released Under LGPL - original licence link has changed is not relivant.
28343  *
28344  * Fork - LGPL
28345  * <script type="text/javascript">
28346  */
28347  
28348  /**
28349  * @class Roo.form.Action
28350  * Internal Class used to handle form actions
28351  * @constructor
28352  * @param {Roo.form.BasicForm} el The form element or its id
28353  * @param {Object} config Configuration options
28354  */
28355  
28356  
28357 // define the action interface
28358 Roo.form.Action = function(form, options){
28359     this.form = form;
28360     this.options = options || {};
28361 };
28362 /**
28363  * Client Validation Failed
28364  * @const 
28365  */
28366 Roo.form.Action.CLIENT_INVALID = 'client';
28367 /**
28368  * Server Validation Failed
28369  * @const 
28370  */
28371  Roo.form.Action.SERVER_INVALID = 'server';
28372  /**
28373  * Connect to Server Failed
28374  * @const 
28375  */
28376 Roo.form.Action.CONNECT_FAILURE = 'connect';
28377 /**
28378  * Reading Data from Server Failed
28379  * @const 
28380  */
28381 Roo.form.Action.LOAD_FAILURE = 'load';
28382
28383 Roo.form.Action.prototype = {
28384     type : 'default',
28385     failureType : undefined,
28386     response : undefined,
28387     result : undefined,
28388
28389     // interface method
28390     run : function(options){
28391
28392     },
28393
28394     // interface method
28395     success : function(response){
28396
28397     },
28398
28399     // interface method
28400     handleResponse : function(response){
28401
28402     },
28403
28404     // default connection failure
28405     failure : function(response){
28406         
28407         this.response = response;
28408         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28409         this.form.afterAction(this, false);
28410     },
28411
28412     processResponse : function(response){
28413         this.response = response;
28414         if(!response.responseText){
28415             return true;
28416         }
28417         this.result = this.handleResponse(response);
28418         return this.result;
28419     },
28420
28421     // utility functions used internally
28422     getUrl : function(appendParams){
28423         var url = this.options.url || this.form.url || this.form.el.dom.action;
28424         if(appendParams){
28425             var p = this.getParams();
28426             if(p){
28427                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
28428             }
28429         }
28430         return url;
28431     },
28432
28433     getMethod : function(){
28434         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
28435     },
28436
28437     getParams : function(){
28438         var bp = this.form.baseParams;
28439         var p = this.options.params;
28440         if(p){
28441             if(typeof p == "object"){
28442                 p = Roo.urlEncode(Roo.applyIf(p, bp));
28443             }else if(typeof p == 'string' && bp){
28444                 p += '&' + Roo.urlEncode(bp);
28445             }
28446         }else if(bp){
28447             p = Roo.urlEncode(bp);
28448         }
28449         return p;
28450     },
28451
28452     createCallback : function(){
28453         return {
28454             success: this.success,
28455             failure: this.failure,
28456             scope: this,
28457             timeout: (this.form.timeout*1000),
28458             upload: this.form.fileUpload ? this.success : undefined
28459         };
28460     }
28461 };
28462
28463 Roo.form.Action.Submit = function(form, options){
28464     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
28465 };
28466
28467 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
28468     type : 'submit',
28469
28470     haveProgress : false,
28471     uploadComplete : false,
28472     
28473     // uploadProgress indicator.
28474     uploadProgress : function()
28475     {
28476         if (!this.form.progressUrl) {
28477             return;
28478         }
28479         
28480         if (!this.haveProgress) {
28481             Roo.MessageBox.progress("Uploading", "Uploading");
28482         }
28483         if (this.uploadComplete) {
28484            Roo.MessageBox.hide();
28485            return;
28486         }
28487         
28488         this.haveProgress = true;
28489    
28490         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
28491         
28492         var c = new Roo.data.Connection();
28493         c.request({
28494             url : this.form.progressUrl,
28495             params: {
28496                 id : uid
28497             },
28498             method: 'GET',
28499             success : function(req){
28500                //console.log(data);
28501                 var rdata = false;
28502                 var edata;
28503                 try  {
28504                    rdata = Roo.decode(req.responseText)
28505                 } catch (e) {
28506                     Roo.log("Invalid data from server..");
28507                     Roo.log(edata);
28508                     return;
28509                 }
28510                 if (!rdata || !rdata.success) {
28511                     Roo.log(rdata);
28512                     return;
28513                 }
28514                 var data = rdata.data;
28515                 
28516                 if (this.uploadComplete) {
28517                    Roo.MessageBox.hide();
28518                    return;
28519                 }
28520                    
28521                 if (data){
28522                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
28523                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
28524                     );
28525                 }
28526                 this.uploadProgress.defer(2000,this);
28527             },
28528        
28529             failure: function(data) {
28530                 Roo.log('progress url failed ');
28531                 Roo.log(data);
28532             },
28533             scope : this
28534         });
28535            
28536     },
28537     
28538     
28539     run : function()
28540     {
28541         // run get Values on the form, so it syncs any secondary forms.
28542         this.form.getValues();
28543         
28544         var o = this.options;
28545         var method = this.getMethod();
28546         var isPost = method == 'POST';
28547         if(o.clientValidation === false || this.form.isValid()){
28548             
28549             if (this.form.progressUrl) {
28550                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
28551                     (new Date() * 1) + '' + Math.random());
28552                     
28553             } 
28554             
28555             
28556             Roo.Ajax.request(Roo.apply(this.createCallback(), {
28557                 form:this.form.el.dom,
28558                 url:this.getUrl(!isPost),
28559                 method: method,
28560                 params:isPost ? this.getParams() : null,
28561                 isUpload: this.form.fileUpload
28562             }));
28563             
28564             this.uploadProgress();
28565
28566         }else if (o.clientValidation !== false){ // client validation failed
28567             this.failureType = Roo.form.Action.CLIENT_INVALID;
28568             this.form.afterAction(this, false);
28569         }
28570     },
28571
28572     success : function(response)
28573     {
28574         this.uploadComplete= true;
28575         if (this.haveProgress) {
28576             Roo.MessageBox.hide();
28577         }
28578         
28579         
28580         var result = this.processResponse(response);
28581         if(result === true || result.success){
28582             this.form.afterAction(this, true);
28583             return;
28584         }
28585         if(result.errors){
28586             this.form.markInvalid(result.errors);
28587             this.failureType = Roo.form.Action.SERVER_INVALID;
28588         }
28589         this.form.afterAction(this, false);
28590     },
28591     failure : function(response)
28592     {
28593         this.uploadComplete= true;
28594         if (this.haveProgress) {
28595             Roo.MessageBox.hide();
28596         }
28597         
28598         this.response = response;
28599         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28600         this.form.afterAction(this, false);
28601     },
28602     
28603     handleResponse : function(response){
28604         if(this.form.errorReader){
28605             var rs = this.form.errorReader.read(response);
28606             var errors = [];
28607             if(rs.records){
28608                 for(var i = 0, len = rs.records.length; i < len; i++) {
28609                     var r = rs.records[i];
28610                     errors[i] = r.data;
28611                 }
28612             }
28613             if(errors.length < 1){
28614                 errors = null;
28615             }
28616             return {
28617                 success : rs.success,
28618                 errors : errors
28619             };
28620         }
28621         var ret = false;
28622         try {
28623             ret = Roo.decode(response.responseText);
28624         } catch (e) {
28625             ret = {
28626                 success: false,
28627                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
28628                 errors : []
28629             };
28630         }
28631         return ret;
28632         
28633     }
28634 });
28635
28636
28637 Roo.form.Action.Load = function(form, options){
28638     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
28639     this.reader = this.form.reader;
28640 };
28641
28642 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
28643     type : 'load',
28644
28645     run : function(){
28646         
28647         Roo.Ajax.request(Roo.apply(
28648                 this.createCallback(), {
28649                     method:this.getMethod(),
28650                     url:this.getUrl(false),
28651                     params:this.getParams()
28652         }));
28653     },
28654
28655     success : function(response){
28656         
28657         var result = this.processResponse(response);
28658         if(result === true || !result.success || !result.data){
28659             this.failureType = Roo.form.Action.LOAD_FAILURE;
28660             this.form.afterAction(this, false);
28661             return;
28662         }
28663         this.form.clearInvalid();
28664         this.form.setValues(result.data);
28665         this.form.afterAction(this, true);
28666     },
28667
28668     handleResponse : function(response){
28669         if(this.form.reader){
28670             var rs = this.form.reader.read(response);
28671             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
28672             return {
28673                 success : rs.success,
28674                 data : data
28675             };
28676         }
28677         return Roo.decode(response.responseText);
28678     }
28679 });
28680
28681 Roo.form.Action.ACTION_TYPES = {
28682     'load' : Roo.form.Action.Load,
28683     'submit' : Roo.form.Action.Submit
28684 };/*
28685  * Based on:
28686  * Ext JS Library 1.1.1
28687  * Copyright(c) 2006-2007, Ext JS, LLC.
28688  *
28689  * Originally Released Under LGPL - original licence link has changed is not relivant.
28690  *
28691  * Fork - LGPL
28692  * <script type="text/javascript">
28693  */
28694  
28695 /**
28696  * @class Roo.form.Layout
28697  * @extends Roo.Component
28698  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
28699  * @constructor
28700  * @param {Object} config Configuration options
28701  */
28702 Roo.form.Layout = function(config){
28703     var xitems = [];
28704     if (config.items) {
28705         xitems = config.items;
28706         delete config.items;
28707     }
28708     Roo.form.Layout.superclass.constructor.call(this, config);
28709     this.stack = [];
28710     Roo.each(xitems, this.addxtype, this);
28711      
28712 };
28713
28714 Roo.extend(Roo.form.Layout, Roo.Component, {
28715     /**
28716      * @cfg {String/Object} autoCreate
28717      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
28718      */
28719     /**
28720      * @cfg {String/Object/Function} style
28721      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
28722      * a function which returns such a specification.
28723      */
28724     /**
28725      * @cfg {String} labelAlign
28726      * Valid values are "left," "top" and "right" (defaults to "left")
28727      */
28728     /**
28729      * @cfg {Number} labelWidth
28730      * Fixed width in pixels of all field labels (defaults to undefined)
28731      */
28732     /**
28733      * @cfg {Boolean} clear
28734      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
28735      */
28736     clear : true,
28737     /**
28738      * @cfg {String} labelSeparator
28739      * The separator to use after field labels (defaults to ':')
28740      */
28741     labelSeparator : ':',
28742     /**
28743      * @cfg {Boolean} hideLabels
28744      * True to suppress the display of field labels in this layout (defaults to false)
28745      */
28746     hideLabels : false,
28747
28748     // private
28749     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
28750     
28751     isLayout : true,
28752     
28753     // private
28754     onRender : function(ct, position){
28755         if(this.el){ // from markup
28756             this.el = Roo.get(this.el);
28757         }else {  // generate
28758             var cfg = this.getAutoCreate();
28759             this.el = ct.createChild(cfg, position);
28760         }
28761         if(this.style){
28762             this.el.applyStyles(this.style);
28763         }
28764         if(this.labelAlign){
28765             this.el.addClass('x-form-label-'+this.labelAlign);
28766         }
28767         if(this.hideLabels){
28768             this.labelStyle = "display:none";
28769             this.elementStyle = "padding-left:0;";
28770         }else{
28771             if(typeof this.labelWidth == 'number'){
28772                 this.labelStyle = "width:"+this.labelWidth+"px;";
28773                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
28774             }
28775             if(this.labelAlign == 'top'){
28776                 this.labelStyle = "width:auto;";
28777                 this.elementStyle = "padding-left:0;";
28778             }
28779         }
28780         var stack = this.stack;
28781         var slen = stack.length;
28782         if(slen > 0){
28783             if(!this.fieldTpl){
28784                 var t = new Roo.Template(
28785                     '<div class="x-form-item {5}">',
28786                         '<label for="{0}" style="{2}">{1}{4}</label>',
28787                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28788                         '</div>',
28789                     '</div><div class="x-form-clear-left"></div>'
28790                 );
28791                 t.disableFormats = true;
28792                 t.compile();
28793                 Roo.form.Layout.prototype.fieldTpl = t;
28794             }
28795             for(var i = 0; i < slen; i++) {
28796                 if(stack[i].isFormField){
28797                     this.renderField(stack[i]);
28798                 }else{
28799                     this.renderComponent(stack[i]);
28800                 }
28801             }
28802         }
28803         if(this.clear){
28804             this.el.createChild({cls:'x-form-clear'});
28805         }
28806     },
28807
28808     // private
28809     renderField : function(f){
28810         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
28811                f.id, //0
28812                f.fieldLabel, //1
28813                f.labelStyle||this.labelStyle||'', //2
28814                this.elementStyle||'', //3
28815                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
28816                f.itemCls||this.itemCls||''  //5
28817        ], true).getPrevSibling());
28818     },
28819
28820     // private
28821     renderComponent : function(c){
28822         c.render(c.isLayout ? this.el : this.el.createChild());    
28823     },
28824     /**
28825      * Adds a object form elements (using the xtype property as the factory method.)
28826      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
28827      * @param {Object} config 
28828      */
28829     addxtype : function(o)
28830     {
28831         // create the lement.
28832         o.form = this.form;
28833         var fe = Roo.factory(o, Roo.form);
28834         this.form.allItems.push(fe);
28835         this.stack.push(fe);
28836         
28837         if (fe.isFormField) {
28838             this.form.items.add(fe);
28839         }
28840          
28841         return fe;
28842     }
28843 });
28844
28845 /**
28846  * @class Roo.form.Column
28847  * @extends Roo.form.Layout
28848  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
28849  * @constructor
28850  * @param {Object} config Configuration options
28851  */
28852 Roo.form.Column = function(config){
28853     Roo.form.Column.superclass.constructor.call(this, config);
28854 };
28855
28856 Roo.extend(Roo.form.Column, Roo.form.Layout, {
28857     /**
28858      * @cfg {Number/String} width
28859      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28860      */
28861     /**
28862      * @cfg {String/Object} autoCreate
28863      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
28864      */
28865
28866     // private
28867     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
28868
28869     // private
28870     onRender : function(ct, position){
28871         Roo.form.Column.superclass.onRender.call(this, ct, position);
28872         if(this.width){
28873             this.el.setWidth(this.width);
28874         }
28875     }
28876 });
28877
28878
28879 /**
28880  * @class Roo.form.Row
28881  * @extends Roo.form.Layout
28882  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
28883  * @constructor
28884  * @param {Object} config Configuration options
28885  */
28886
28887  
28888 Roo.form.Row = function(config){
28889     Roo.form.Row.superclass.constructor.call(this, config);
28890 };
28891  
28892 Roo.extend(Roo.form.Row, Roo.form.Layout, {
28893       /**
28894      * @cfg {Number/String} width
28895      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28896      */
28897     /**
28898      * @cfg {Number/String} height
28899      * The fixed height of the column in pixels or CSS value (defaults to "auto")
28900      */
28901     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
28902     
28903     padWidth : 20,
28904     // private
28905     onRender : function(ct, position){
28906         //console.log('row render');
28907         if(!this.rowTpl){
28908             var t = new Roo.Template(
28909                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
28910                     '<label for="{0}" style="{2}">{1}{4}</label>',
28911                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28912                     '</div>',
28913                 '</div>'
28914             );
28915             t.disableFormats = true;
28916             t.compile();
28917             Roo.form.Layout.prototype.rowTpl = t;
28918         }
28919         this.fieldTpl = this.rowTpl;
28920         
28921         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
28922         var labelWidth = 100;
28923         
28924         if ((this.labelAlign != 'top')) {
28925             if (typeof this.labelWidth == 'number') {
28926                 labelWidth = this.labelWidth
28927             }
28928             this.padWidth =  20 + labelWidth;
28929             
28930         }
28931         
28932         Roo.form.Column.superclass.onRender.call(this, ct, position);
28933         if(this.width){
28934             this.el.setWidth(this.width);
28935         }
28936         if(this.height){
28937             this.el.setHeight(this.height);
28938         }
28939     },
28940     
28941     // private
28942     renderField : function(f){
28943         f.fieldEl = this.fieldTpl.append(this.el, [
28944                f.id, f.fieldLabel,
28945                f.labelStyle||this.labelStyle||'',
28946                this.elementStyle||'',
28947                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
28948                f.itemCls||this.itemCls||'',
28949                f.width ? f.width + this.padWidth : 160 + this.padWidth
28950        ],true);
28951     }
28952 });
28953  
28954
28955 /**
28956  * @class Roo.form.FieldSet
28957  * @extends Roo.form.Layout
28958  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
28959  * @constructor
28960  * @param {Object} config Configuration options
28961  */
28962 Roo.form.FieldSet = function(config){
28963     Roo.form.FieldSet.superclass.constructor.call(this, config);
28964 };
28965
28966 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
28967     /**
28968      * @cfg {String} legend
28969      * The text to display as the legend for the FieldSet (defaults to '')
28970      */
28971     /**
28972      * @cfg {String/Object} autoCreate
28973      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
28974      */
28975
28976     // private
28977     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
28978
28979     // private
28980     onRender : function(ct, position){
28981         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
28982         if(this.legend){
28983             this.setLegend(this.legend);
28984         }
28985     },
28986
28987     // private
28988     setLegend : function(text){
28989         if(this.rendered){
28990             this.el.child('legend').update(text);
28991         }
28992     }
28993 });/*
28994  * Based on:
28995  * Ext JS Library 1.1.1
28996  * Copyright(c) 2006-2007, Ext JS, LLC.
28997  *
28998  * Originally Released Under LGPL - original licence link has changed is not relivant.
28999  *
29000  * Fork - LGPL
29001  * <script type="text/javascript">
29002  */
29003 /**
29004  * @class Roo.form.VTypes
29005  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29006  * @singleton
29007  */
29008 Roo.form.VTypes = function(){
29009     // closure these in so they are only created once.
29010     var alpha = /^[a-zA-Z_]+$/;
29011     var alphanum = /^[a-zA-Z0-9_]+$/;
29012     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29013     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29014
29015     // All these messages and functions are configurable
29016     return {
29017         /**
29018          * The function used to validate email addresses
29019          * @param {String} value The email address
29020          */
29021         'email' : function(v){
29022             return email.test(v);
29023         },
29024         /**
29025          * The error text to display when the email validation function returns false
29026          * @type String
29027          */
29028         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29029         /**
29030          * The keystroke filter mask to be applied on email input
29031          * @type RegExp
29032          */
29033         'emailMask' : /[a-z0-9_\.\-@]/i,
29034
29035         /**
29036          * The function used to validate URLs
29037          * @param {String} value The URL
29038          */
29039         'url' : function(v){
29040             return url.test(v);
29041         },
29042         /**
29043          * The error text to display when the url validation function returns false
29044          * @type String
29045          */
29046         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29047         
29048         /**
29049          * The function used to validate alpha values
29050          * @param {String} value The value
29051          */
29052         'alpha' : function(v){
29053             return alpha.test(v);
29054         },
29055         /**
29056          * The error text to display when the alpha validation function returns false
29057          * @type String
29058          */
29059         'alphaText' : 'This field should only contain letters and _',
29060         /**
29061          * The keystroke filter mask to be applied on alpha input
29062          * @type RegExp
29063          */
29064         'alphaMask' : /[a-z_]/i,
29065
29066         /**
29067          * The function used to validate alphanumeric values
29068          * @param {String} value The value
29069          */
29070         'alphanum' : function(v){
29071             return alphanum.test(v);
29072         },
29073         /**
29074          * The error text to display when the alphanumeric validation function returns false
29075          * @type String
29076          */
29077         'alphanumText' : 'This field should only contain letters, numbers and _',
29078         /**
29079          * The keystroke filter mask to be applied on alphanumeric input
29080          * @type RegExp
29081          */
29082         'alphanumMask' : /[a-z0-9_]/i
29083     };
29084 }();//<script type="text/javascript">
29085
29086 /**
29087  * @class Roo.form.FCKeditor
29088  * @extends Roo.form.TextArea
29089  * Wrapper around the FCKEditor http://www.fckeditor.net
29090  * @constructor
29091  * Creates a new FCKeditor
29092  * @param {Object} config Configuration options
29093  */
29094 Roo.form.FCKeditor = function(config){
29095     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29096     this.addEvents({
29097          /**
29098          * @event editorinit
29099          * Fired when the editor is initialized - you can add extra handlers here..
29100          * @param {FCKeditor} this
29101          * @param {Object} the FCK object.
29102          */
29103         editorinit : true
29104     });
29105     
29106     
29107 };
29108 Roo.form.FCKeditor.editors = { };
29109 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29110 {
29111     //defaultAutoCreate : {
29112     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29113     //},
29114     // private
29115     /**
29116      * @cfg {Object} fck options - see fck manual for details.
29117      */
29118     fckconfig : false,
29119     
29120     /**
29121      * @cfg {Object} fck toolbar set (Basic or Default)
29122      */
29123     toolbarSet : 'Basic',
29124     /**
29125      * @cfg {Object} fck BasePath
29126      */ 
29127     basePath : '/fckeditor/',
29128     
29129     
29130     frame : false,
29131     
29132     value : '',
29133     
29134    
29135     onRender : function(ct, position)
29136     {
29137         if(!this.el){
29138             this.defaultAutoCreate = {
29139                 tag: "textarea",
29140                 style:"width:300px;height:60px;",
29141                 autocomplete: "off"
29142             };
29143         }
29144         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29145         /*
29146         if(this.grow){
29147             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29148             if(this.preventScrollbars){
29149                 this.el.setStyle("overflow", "hidden");
29150             }
29151             this.el.setHeight(this.growMin);
29152         }
29153         */
29154         //console.log('onrender' + this.getId() );
29155         Roo.form.FCKeditor.editors[this.getId()] = this;
29156          
29157
29158         this.replaceTextarea() ;
29159         
29160     },
29161     
29162     getEditor : function() {
29163         return this.fckEditor;
29164     },
29165     /**
29166      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29167      * @param {Mixed} value The value to set
29168      */
29169     
29170     
29171     setValue : function(value)
29172     {
29173         //console.log('setValue: ' + value);
29174         
29175         if(typeof(value) == 'undefined') { // not sure why this is happending...
29176             return;
29177         }
29178         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29179         
29180         //if(!this.el || !this.getEditor()) {
29181         //    this.value = value;
29182             //this.setValue.defer(100,this,[value]);    
29183         //    return;
29184         //} 
29185         
29186         if(!this.getEditor()) {
29187             return;
29188         }
29189         
29190         this.getEditor().SetData(value);
29191         
29192         //
29193
29194     },
29195
29196     /**
29197      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29198      * @return {Mixed} value The field value
29199      */
29200     getValue : function()
29201     {
29202         
29203         if (this.frame && this.frame.dom.style.display == 'none') {
29204             return Roo.form.FCKeditor.superclass.getValue.call(this);
29205         }
29206         
29207         if(!this.el || !this.getEditor()) {
29208            
29209            // this.getValue.defer(100,this); 
29210             return this.value;
29211         }
29212        
29213         
29214         var value=this.getEditor().GetData();
29215         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29216         return Roo.form.FCKeditor.superclass.getValue.call(this);
29217         
29218
29219     },
29220
29221     /**
29222      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29223      * @return {Mixed} value The field value
29224      */
29225     getRawValue : function()
29226     {
29227         if (this.frame && this.frame.dom.style.display == 'none') {
29228             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29229         }
29230         
29231         if(!this.el || !this.getEditor()) {
29232             //this.getRawValue.defer(100,this); 
29233             return this.value;
29234             return;
29235         }
29236         
29237         
29238         
29239         var value=this.getEditor().GetData();
29240         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29241         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29242          
29243     },
29244     
29245     setSize : function(w,h) {
29246         
29247         
29248         
29249         //if (this.frame && this.frame.dom.style.display == 'none') {
29250         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29251         //    return;
29252         //}
29253         //if(!this.el || !this.getEditor()) {
29254         //    this.setSize.defer(100,this, [w,h]); 
29255         //    return;
29256         //}
29257         
29258         
29259         
29260         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29261         
29262         this.frame.dom.setAttribute('width', w);
29263         this.frame.dom.setAttribute('height', h);
29264         this.frame.setSize(w,h);
29265         
29266     },
29267     
29268     toggleSourceEdit : function(value) {
29269         
29270       
29271          
29272         this.el.dom.style.display = value ? '' : 'none';
29273         this.frame.dom.style.display = value ?  'none' : '';
29274         
29275     },
29276     
29277     
29278     focus: function(tag)
29279     {
29280         if (this.frame.dom.style.display == 'none') {
29281             return Roo.form.FCKeditor.superclass.focus.call(this);
29282         }
29283         if(!this.el || !this.getEditor()) {
29284             this.focus.defer(100,this, [tag]); 
29285             return;
29286         }
29287         
29288         
29289         
29290         
29291         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
29292         this.getEditor().Focus();
29293         if (tgs.length) {
29294             if (!this.getEditor().Selection.GetSelection()) {
29295                 this.focus.defer(100,this, [tag]); 
29296                 return;
29297             }
29298             
29299             
29300             var r = this.getEditor().EditorDocument.createRange();
29301             r.setStart(tgs[0],0);
29302             r.setEnd(tgs[0],0);
29303             this.getEditor().Selection.GetSelection().removeAllRanges();
29304             this.getEditor().Selection.GetSelection().addRange(r);
29305             this.getEditor().Focus();
29306         }
29307         
29308     },
29309     
29310     
29311     
29312     replaceTextarea : function()
29313     {
29314         if ( document.getElementById( this.getId() + '___Frame' ) )
29315             return ;
29316         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
29317         //{
29318             // We must check the elements firstly using the Id and then the name.
29319         var oTextarea = document.getElementById( this.getId() );
29320         
29321         var colElementsByName = document.getElementsByName( this.getId() ) ;
29322          
29323         oTextarea.style.display = 'none' ;
29324
29325         if ( oTextarea.tabIndex ) {            
29326             this.TabIndex = oTextarea.tabIndex ;
29327         }
29328         
29329         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
29330         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
29331         this.frame = Roo.get(this.getId() + '___Frame')
29332     },
29333     
29334     _getConfigHtml : function()
29335     {
29336         var sConfig = '' ;
29337
29338         for ( var o in this.fckconfig ) {
29339             sConfig += sConfig.length > 0  ? '&amp;' : '';
29340             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
29341         }
29342
29343         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
29344     },
29345     
29346     
29347     _getIFrameHtml : function()
29348     {
29349         var sFile = 'fckeditor.html' ;
29350         /* no idea what this is about..
29351         try
29352         {
29353             if ( (/fcksource=true/i).test( window.top.location.search ) )
29354                 sFile = 'fckeditor.original.html' ;
29355         }
29356         catch (e) { 
29357         */
29358
29359         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
29360         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
29361         
29362         
29363         var html = '<iframe id="' + this.getId() +
29364             '___Frame" src="' + sLink +
29365             '" width="' + this.width +
29366             '" height="' + this.height + '"' +
29367             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
29368             ' frameborder="0" scrolling="no"></iframe>' ;
29369
29370         return html ;
29371     },
29372     
29373     _insertHtmlBefore : function( html, element )
29374     {
29375         if ( element.insertAdjacentHTML )       {
29376             // IE
29377             element.insertAdjacentHTML( 'beforeBegin', html ) ;
29378         } else { // Gecko
29379             var oRange = document.createRange() ;
29380             oRange.setStartBefore( element ) ;
29381             var oFragment = oRange.createContextualFragment( html );
29382             element.parentNode.insertBefore( oFragment, element ) ;
29383         }
29384     }
29385     
29386     
29387   
29388     
29389     
29390     
29391     
29392
29393 });
29394
29395 //Roo.reg('fckeditor', Roo.form.FCKeditor);
29396
29397 function FCKeditor_OnComplete(editorInstance){
29398     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
29399     f.fckEditor = editorInstance;
29400     //console.log("loaded");
29401     f.fireEvent('editorinit', f, editorInstance);
29402
29403   
29404
29405  
29406
29407
29408
29409
29410
29411
29412
29413
29414
29415
29416
29417
29418
29419
29420
29421 //<script type="text/javascript">
29422 /**
29423  * @class Roo.form.GridField
29424  * @extends Roo.form.Field
29425  * Embed a grid (or editable grid into a form)
29426  * STATUS ALPHA
29427  * 
29428  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
29429  * it needs 
29430  * xgrid.store = Roo.data.Store
29431  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
29432  * xgrid.store.reader = Roo.data.JsonReader 
29433  * 
29434  * 
29435  * @constructor
29436  * Creates a new GridField
29437  * @param {Object} config Configuration options
29438  */
29439 Roo.form.GridField = function(config){
29440     Roo.form.GridField.superclass.constructor.call(this, config);
29441      
29442 };
29443
29444 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
29445     /**
29446      * @cfg {Number} width  - used to restrict width of grid..
29447      */
29448     width : 100,
29449     /**
29450      * @cfg {Number} height - used to restrict height of grid..
29451      */
29452     height : 50,
29453      /**
29454      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
29455          * 
29456          *}
29457      */
29458     xgrid : false, 
29459     /**
29460      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29461      * {tag: "input", type: "checkbox", autocomplete: "off"})
29462      */
29463    // defaultAutoCreate : { tag: 'div' },
29464     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29465     /**
29466      * @cfg {String} addTitle Text to include for adding a title.
29467      */
29468     addTitle : false,
29469     //
29470     onResize : function(){
29471         Roo.form.Field.superclass.onResize.apply(this, arguments);
29472     },
29473
29474     initEvents : function(){
29475         // Roo.form.Checkbox.superclass.initEvents.call(this);
29476         // has no events...
29477        
29478     },
29479
29480
29481     getResizeEl : function(){
29482         return this.wrap;
29483     },
29484
29485     getPositionEl : function(){
29486         return this.wrap;
29487     },
29488
29489     // private
29490     onRender : function(ct, position){
29491         
29492         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
29493         var style = this.style;
29494         delete this.style;
29495         
29496         Roo.form.GridField.superclass.onRender.call(this, ct, position);
29497         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
29498         this.viewEl = this.wrap.createChild({ tag: 'div' });
29499         if (style) {
29500             this.viewEl.applyStyles(style);
29501         }
29502         if (this.width) {
29503             this.viewEl.setWidth(this.width);
29504         }
29505         if (this.height) {
29506             this.viewEl.setHeight(this.height);
29507         }
29508         //if(this.inputValue !== undefined){
29509         //this.setValue(this.value);
29510         
29511         
29512         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
29513         
29514         
29515         this.grid.render();
29516         this.grid.getDataSource().on('remove', this.refreshValue, this);
29517         this.grid.getDataSource().on('update', this.refreshValue, this);
29518         this.grid.on('afteredit', this.refreshValue, this);
29519  
29520     },
29521      
29522     
29523     /**
29524      * Sets the value of the item. 
29525      * @param {String} either an object  or a string..
29526      */
29527     setValue : function(v){
29528         //this.value = v;
29529         v = v || []; // empty set..
29530         // this does not seem smart - it really only affects memoryproxy grids..
29531         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
29532             var ds = this.grid.getDataSource();
29533             // assumes a json reader..
29534             var data = {}
29535             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
29536             ds.loadData( data);
29537         }
29538         // clear selection so it does not get stale.
29539         if (this.grid.sm) { 
29540             this.grid.sm.clearSelections();
29541         }
29542         
29543         Roo.form.GridField.superclass.setValue.call(this, v);
29544         this.refreshValue();
29545         // should load data in the grid really....
29546     },
29547     
29548     // private
29549     refreshValue: function() {
29550          var val = [];
29551         this.grid.getDataSource().each(function(r) {
29552             val.push(r.data);
29553         });
29554         this.el.dom.value = Roo.encode(val);
29555     }
29556     
29557      
29558     
29559     
29560 });/*
29561  * Based on:
29562  * Ext JS Library 1.1.1
29563  * Copyright(c) 2006-2007, Ext JS, LLC.
29564  *
29565  * Originally Released Under LGPL - original licence link has changed is not relivant.
29566  *
29567  * Fork - LGPL
29568  * <script type="text/javascript">
29569  */
29570 /**
29571  * @class Roo.form.DisplayField
29572  * @extends Roo.form.Field
29573  * A generic Field to display non-editable data.
29574  * @constructor
29575  * Creates a new Display Field item.
29576  * @param {Object} config Configuration options
29577  */
29578 Roo.form.DisplayField = function(config){
29579     Roo.form.DisplayField.superclass.constructor.call(this, config);
29580     
29581 };
29582
29583 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
29584     inputType:      'hidden',
29585     allowBlank:     true,
29586     readOnly:         true,
29587     
29588  
29589     /**
29590      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29591      */
29592     focusClass : undefined,
29593     /**
29594      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29595      */
29596     fieldClass: 'x-form-field',
29597     
29598      /**
29599      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
29600      */
29601     valueRenderer: undefined,
29602     
29603     width: 100,
29604     /**
29605      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29606      * {tag: "input", type: "checkbox", autocomplete: "off"})
29607      */
29608      
29609  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29610
29611     onResize : function(){
29612         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
29613         
29614     },
29615
29616     initEvents : function(){
29617         // Roo.form.Checkbox.superclass.initEvents.call(this);
29618         // has no events...
29619        
29620     },
29621
29622
29623     getResizeEl : function(){
29624         return this.wrap;
29625     },
29626
29627     getPositionEl : function(){
29628         return this.wrap;
29629     },
29630
29631     // private
29632     onRender : function(ct, position){
29633         
29634         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
29635         //if(this.inputValue !== undefined){
29636         this.wrap = this.el.wrap();
29637         
29638         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
29639         
29640         if (this.bodyStyle) {
29641             this.viewEl.applyStyles(this.bodyStyle);
29642         }
29643         //this.viewEl.setStyle('padding', '2px');
29644         
29645         this.setValue(this.value);
29646         
29647     },
29648 /*
29649     // private
29650     initValue : Roo.emptyFn,
29651
29652   */
29653
29654         // private
29655     onClick : function(){
29656         
29657     },
29658
29659     /**
29660      * Sets the checked state of the checkbox.
29661      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
29662      */
29663     setValue : function(v){
29664         this.value = v;
29665         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
29666         // this might be called before we have a dom element..
29667         if (!this.viewEl) {
29668             return;
29669         }
29670         this.viewEl.dom.innerHTML = html;
29671         Roo.form.DisplayField.superclass.setValue.call(this, v);
29672
29673     }
29674 });/*
29675  * 
29676  * Licence- LGPL
29677  * 
29678  */
29679
29680 /**
29681  * @class Roo.form.DayPicker
29682  * @extends Roo.form.Field
29683  * A Day picker show [M] [T] [W] ....
29684  * @constructor
29685  * Creates a new Day Picker
29686  * @param {Object} config Configuration options
29687  */
29688 Roo.form.DayPicker= function(config){
29689     Roo.form.DayPicker.superclass.constructor.call(this, config);
29690      
29691 };
29692
29693 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
29694     /**
29695      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29696      */
29697     focusClass : undefined,
29698     /**
29699      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29700      */
29701     fieldClass: "x-form-field",
29702    
29703     /**
29704      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29705      * {tag: "input", type: "checkbox", autocomplete: "off"})
29706      */
29707     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
29708     
29709    
29710     actionMode : 'viewEl', 
29711     //
29712     // private
29713  
29714     inputType : 'hidden',
29715     
29716      
29717     inputElement: false, // real input element?
29718     basedOn: false, // ????
29719     
29720     isFormField: true, // not sure where this is needed!!!!
29721
29722     onResize : function(){
29723         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
29724         if(!this.boxLabel){
29725             this.el.alignTo(this.wrap, 'c-c');
29726         }
29727     },
29728
29729     initEvents : function(){
29730         Roo.form.Checkbox.superclass.initEvents.call(this);
29731         this.el.on("click", this.onClick,  this);
29732         this.el.on("change", this.onClick,  this);
29733     },
29734
29735
29736     getResizeEl : function(){
29737         return this.wrap;
29738     },
29739
29740     getPositionEl : function(){
29741         return this.wrap;
29742     },
29743
29744     
29745     // private
29746     onRender : function(ct, position){
29747         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
29748        
29749         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
29750         
29751         var r1 = '<table><tr>';
29752         var r2 = '<tr class="x-form-daypick-icons">';
29753         for (var i=0; i < 7; i++) {
29754             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
29755             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
29756         }
29757         
29758         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
29759         viewEl.select('img').on('click', this.onClick, this);
29760         this.viewEl = viewEl;   
29761         
29762         
29763         // this will not work on Chrome!!!
29764         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
29765         this.el.on('propertychange', this.setFromHidden,  this);  //ie
29766         
29767         
29768           
29769
29770     },
29771
29772     // private
29773     initValue : Roo.emptyFn,
29774
29775     /**
29776      * Returns the checked state of the checkbox.
29777      * @return {Boolean} True if checked, else false
29778      */
29779     getValue : function(){
29780         return this.el.dom.value;
29781         
29782     },
29783
29784         // private
29785     onClick : function(e){ 
29786         //this.setChecked(!this.checked);
29787         Roo.get(e.target).toggleClass('x-menu-item-checked');
29788         this.refreshValue();
29789         //if(this.el.dom.checked != this.checked){
29790         //    this.setValue(this.el.dom.checked);
29791        // }
29792     },
29793     
29794     // private
29795     refreshValue : function()
29796     {
29797         var val = '';
29798         this.viewEl.select('img',true).each(function(e,i,n)  {
29799             val += e.is(".x-menu-item-checked") ? String(n) : '';
29800         });
29801         this.setValue(val, true);
29802     },
29803
29804     /**
29805      * Sets the checked state of the checkbox.
29806      * On is always based on a string comparison between inputValue and the param.
29807      * @param {Boolean/String} value - the value to set 
29808      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
29809      */
29810     setValue : function(v,suppressEvent){
29811         if (!this.el.dom) {
29812             return;
29813         }
29814         var old = this.el.dom.value ;
29815         this.el.dom.value = v;
29816         if (suppressEvent) {
29817             return ;
29818         }
29819          
29820         // update display..
29821         this.viewEl.select('img',true).each(function(e,i,n)  {
29822             
29823             var on = e.is(".x-menu-item-checked");
29824             var newv = v.indexOf(String(n)) > -1;
29825             if (on != newv) {
29826                 e.toggleClass('x-menu-item-checked');
29827             }
29828             
29829         });
29830         
29831         
29832         this.fireEvent('change', this, v, old);
29833         
29834         
29835     },
29836    
29837     // handle setting of hidden value by some other method!!?!?
29838     setFromHidden: function()
29839     {
29840         if(!this.el){
29841             return;
29842         }
29843         //console.log("SET FROM HIDDEN");
29844         //alert('setFrom hidden');
29845         this.setValue(this.el.dom.value);
29846     },
29847     
29848     onDestroy : function()
29849     {
29850         if(this.viewEl){
29851             Roo.get(this.viewEl).remove();
29852         }
29853          
29854         Roo.form.DayPicker.superclass.onDestroy.call(this);
29855     }
29856
29857 });/*
29858  * RooJS Library 1.1.1
29859  * Copyright(c) 2008-2011  Alan Knowles
29860  *
29861  * License - LGPL
29862  */
29863  
29864
29865 /**
29866  * @class Roo.form.ComboCheck
29867  * @extends Roo.form.ComboBox
29868  * A combobox for multiple select items.
29869  *
29870  * FIXME - could do with a reset button..
29871  * 
29872  * @constructor
29873  * Create a new ComboCheck
29874  * @param {Object} config Configuration options
29875  */
29876 Roo.form.ComboCheck = function(config){
29877     Roo.form.ComboCheck.superclass.constructor.call(this, config);
29878     // should verify some data...
29879     // like
29880     // hiddenName = required..
29881     // displayField = required
29882     // valudField == required
29883     var req= [ 'hiddenName', 'displayField', 'valueField' ];
29884     var _t = this;
29885     Roo.each(req, function(e) {
29886         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
29887             throw "Roo.form.ComboCheck : missing value for: " + e;
29888         }
29889     });
29890     
29891     
29892 };
29893
29894 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
29895      
29896      
29897     editable : false,
29898      
29899     selectedClass: 'x-menu-item-checked', 
29900     
29901     // private
29902     onRender : function(ct, position){
29903         var _t = this;
29904         
29905         
29906         
29907         if(!this.tpl){
29908             var cls = 'x-combo-list';
29909
29910             
29911             this.tpl =  new Roo.Template({
29912                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
29913                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
29914                    '<span>{' + this.displayField + '}</span>' +
29915                     '</div>' 
29916                 
29917             });
29918         }
29919  
29920         
29921         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
29922         this.view.singleSelect = false;
29923         this.view.multiSelect = true;
29924         this.view.toggleSelect = true;
29925         this.pageTb.add(new Roo.Toolbar.Fill(), {
29926             
29927             text: 'Done',
29928             handler: function()
29929             {
29930                 _t.collapse();
29931             }
29932         });
29933     },
29934     
29935     onViewOver : function(e, t){
29936         // do nothing...
29937         return;
29938         
29939     },
29940     
29941     onViewClick : function(doFocus,index){
29942         return;
29943         
29944     },
29945     select: function () {
29946         //Roo.log("SELECT CALLED");
29947     },
29948      
29949     selectByValue : function(xv, scrollIntoView){
29950         var ar = this.getValueArray();
29951         var sels = [];
29952         
29953         Roo.each(ar, function(v) {
29954             if(v === undefined || v === null){
29955                 return;
29956             }
29957             var r = this.findRecord(this.valueField, v);
29958             if(r){
29959                 sels.push(this.store.indexOf(r))
29960                 
29961             }
29962         },this);
29963         this.view.select(sels);
29964         return false;
29965     },
29966     
29967     
29968     
29969     onSelect : function(record, index){
29970        // Roo.log("onselect Called");
29971        // this is only called by the clear button now..
29972         this.view.clearSelections();
29973         this.setValue('[]');
29974         if (this.value != this.valueBefore) {
29975             this.fireEvent('change', this, this.value, this.valueBefore);
29976         }
29977     },
29978     getValueArray : function()
29979     {
29980         var ar = [] ;
29981         
29982         try {
29983             //Roo.log(this.value);
29984             if (typeof(this.value) == 'undefined') {
29985                 return [];
29986             }
29987             var ar = Roo.decode(this.value);
29988             return  ar instanceof Array ? ar : []; //?? valid?
29989             
29990         } catch(e) {
29991             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
29992             return [];
29993         }
29994          
29995     },
29996     expand : function ()
29997     {
29998         Roo.form.ComboCheck.superclass.expand.call(this);
29999         this.valueBefore = this.value;
30000         
30001
30002     },
30003     
30004     collapse : function(){
30005         Roo.form.ComboCheck.superclass.collapse.call(this);
30006         var sl = this.view.getSelectedIndexes();
30007         var st = this.store;
30008         var nv = [];
30009         var tv = [];
30010         var r;
30011         Roo.each(sl, function(i) {
30012             r = st.getAt(i);
30013             nv.push(r.get(this.valueField));
30014         },this);
30015         this.setValue(Roo.encode(nv));
30016         if (this.value != this.valueBefore) {
30017
30018             this.fireEvent('change', this, this.value, this.valueBefore);
30019         }
30020         
30021     },
30022     
30023     setValue : function(v){
30024         // Roo.log(v);
30025         this.value = v;
30026         
30027         var vals = this.getValueArray();
30028         var tv = [];
30029         Roo.each(vals, function(k) {
30030             var r = this.findRecord(this.valueField, k);
30031             if(r){
30032                 tv.push(r.data[this.displayField]);
30033             }else if(this.valueNotFoundText !== undefined){
30034                 tv.push( this.valueNotFoundText );
30035             }
30036         },this);
30037        // Roo.log(tv);
30038         
30039         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30040         this.hiddenField.value = v;
30041         this.value = v;
30042     }
30043     
30044 });//<script type="text/javasscript">
30045  
30046
30047 /**
30048  * @class Roo.DDView
30049  * A DnD enabled version of Roo.View.
30050  * @param {Element/String} container The Element in which to create the View.
30051  * @param {String} tpl The template string used to create the markup for each element of the View
30052  * @param {Object} config The configuration properties. These include all the config options of
30053  * {@link Roo.View} plus some specific to this class.<br>
30054  * <p>
30055  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
30056  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
30057  * <p>
30058  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
30059 .x-view-drag-insert-above {
30060         border-top:1px dotted #3366cc;
30061 }
30062 .x-view-drag-insert-below {
30063         border-bottom:1px dotted #3366cc;
30064 }
30065 </code></pre>
30066  * 
30067  */
30068  
30069 Roo.DDView = function(container, tpl, config) {
30070     Roo.DDView.superclass.constructor.apply(this, arguments);
30071     this.getEl().setStyle("outline", "0px none");
30072     this.getEl().unselectable();
30073     if (this.dragGroup) {
30074                 this.setDraggable(this.dragGroup.split(","));
30075     }
30076     if (this.dropGroup) {
30077                 this.setDroppable(this.dropGroup.split(","));
30078     }
30079     if (this.deletable) {
30080         this.setDeletable();
30081     }
30082     this.isDirtyFlag = false;
30083         this.addEvents({
30084                 "drop" : true
30085         });
30086 };
30087
30088 Roo.extend(Roo.DDView, Roo.View, {
30089 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
30090 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
30091 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
30092 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
30093
30094         isFormField: true,
30095
30096         reset: Roo.emptyFn,
30097         
30098         clearInvalid: Roo.form.Field.prototype.clearInvalid,
30099
30100         validate: function() {
30101                 return true;
30102         },
30103         
30104         destroy: function() {
30105                 this.purgeListeners();
30106                 this.getEl.removeAllListeners();
30107                 this.getEl().remove();
30108                 if (this.dragZone) {
30109                         if (this.dragZone.destroy) {
30110                                 this.dragZone.destroy();
30111                         }
30112                 }
30113                 if (this.dropZone) {
30114                         if (this.dropZone.destroy) {
30115                                 this.dropZone.destroy();
30116                         }
30117                 }
30118         },
30119
30120 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
30121         getName: function() {
30122                 return this.name;
30123         },
30124
30125 /**     Loads the View from a JSON string representing the Records to put into the Store. */
30126         setValue: function(v) {
30127                 if (!this.store) {
30128                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
30129                 }
30130                 var data = {};
30131                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
30132                 this.store.proxy = new Roo.data.MemoryProxy(data);
30133                 this.store.load();
30134         },
30135
30136 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
30137         getValue: function() {
30138                 var result = '(';
30139                 this.store.each(function(rec) {
30140                         result += rec.id + ',';
30141                 });
30142                 return result.substr(0, result.length - 1) + ')';
30143         },
30144         
30145         getIds: function() {
30146                 var i = 0, result = new Array(this.store.getCount());
30147                 this.store.each(function(rec) {
30148                         result[i++] = rec.id;
30149                 });
30150                 return result;
30151         },
30152         
30153         isDirty: function() {
30154                 return this.isDirtyFlag;
30155         },
30156
30157 /**
30158  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
30159  *      whole Element becomes the target, and this causes the drop gesture to append.
30160  */
30161     getTargetFromEvent : function(e) {
30162                 var target = e.getTarget();
30163                 while ((target !== null) && (target.parentNode != this.el.dom)) {
30164                 target = target.parentNode;
30165                 }
30166                 if (!target) {
30167                         target = this.el.dom.lastChild || this.el.dom;
30168                 }
30169                 return target;
30170     },
30171
30172 /**
30173  *      Create the drag data which consists of an object which has the property "ddel" as
30174  *      the drag proxy element. 
30175  */
30176     getDragData : function(e) {
30177         var target = this.findItemFromChild(e.getTarget());
30178                 if(target) {
30179                         this.handleSelection(e);
30180                         var selNodes = this.getSelectedNodes();
30181             var dragData = {
30182                 source: this,
30183                 copy: this.copy || (this.allowCopy && e.ctrlKey),
30184                 nodes: selNodes,
30185                 records: []
30186                         };
30187                         var selectedIndices = this.getSelectedIndexes();
30188                         for (var i = 0; i < selectedIndices.length; i++) {
30189                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
30190                         }
30191                         if (selNodes.length == 1) {
30192                                 dragData.ddel = target.cloneNode(true); // the div element
30193                         } else {
30194                                 var div = document.createElement('div'); // create the multi element drag "ghost"
30195                                 div.className = 'multi-proxy';
30196                                 for (var i = 0, len = selNodes.length; i < len; i++) {
30197                                         div.appendChild(selNodes[i].cloneNode(true));
30198                                 }
30199                                 dragData.ddel = div;
30200                         }
30201             //console.log(dragData)
30202             //console.log(dragData.ddel.innerHTML)
30203                         return dragData;
30204                 }
30205         //console.log('nodragData')
30206                 return false;
30207     },
30208     
30209 /**     Specify to which ddGroup items in this DDView may be dragged. */
30210     setDraggable: function(ddGroup) {
30211         if (ddGroup instanceof Array) {
30212                 Roo.each(ddGroup, this.setDraggable, this);
30213                 return;
30214         }
30215         if (this.dragZone) {
30216                 this.dragZone.addToGroup(ddGroup);
30217         } else {
30218                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
30219                                 containerScroll: true,
30220                                 ddGroup: ddGroup 
30221
30222                         });
30223 //                      Draggability implies selection. DragZone's mousedown selects the element.
30224                         if (!this.multiSelect) { this.singleSelect = true; }
30225
30226 //                      Wire the DragZone's handlers up to methods in *this*
30227                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
30228                 }
30229     },
30230
30231 /**     Specify from which ddGroup this DDView accepts drops. */
30232     setDroppable: function(ddGroup) {
30233         if (ddGroup instanceof Array) {
30234                 Roo.each(ddGroup, this.setDroppable, this);
30235                 return;
30236         }
30237         if (this.dropZone) {
30238                 this.dropZone.addToGroup(ddGroup);
30239         } else {
30240                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
30241                                 containerScroll: true,
30242                                 ddGroup: ddGroup
30243                         });
30244
30245 //                      Wire the DropZone's handlers up to methods in *this*
30246                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
30247                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
30248                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
30249                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
30250                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
30251                 }
30252     },
30253
30254 /**     Decide whether to drop above or below a View node. */
30255     getDropPoint : function(e, n, dd){
30256         if (n == this.el.dom) { return "above"; }
30257                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
30258                 var c = t + (b - t) / 2;
30259                 var y = Roo.lib.Event.getPageY(e);
30260                 if(y <= c) {
30261                         return "above";
30262                 }else{
30263                         return "below";
30264                 }
30265     },
30266
30267     onNodeEnter : function(n, dd, e, data){
30268                 return false;
30269     },
30270     
30271     onNodeOver : function(n, dd, e, data){
30272                 var pt = this.getDropPoint(e, n, dd);
30273                 // set the insert point style on the target node
30274                 var dragElClass = this.dropNotAllowed;
30275                 if (pt) {
30276                         var targetElClass;
30277                         if (pt == "above"){
30278                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
30279                                 targetElClass = "x-view-drag-insert-above";
30280                         } else {
30281                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
30282                                 targetElClass = "x-view-drag-insert-below";
30283                         }
30284                         if (this.lastInsertClass != targetElClass){
30285                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
30286                                 this.lastInsertClass = targetElClass;
30287                         }
30288                 }
30289                 return dragElClass;
30290         },
30291
30292     onNodeOut : function(n, dd, e, data){
30293                 this.removeDropIndicators(n);
30294     },
30295
30296     onNodeDrop : function(n, dd, e, data){
30297         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
30298                 return false;
30299         }
30300         var pt = this.getDropPoint(e, n, dd);
30301                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
30302                 if (pt == "below") { insertAt++; }
30303                 for (var i = 0; i < data.records.length; i++) {
30304                         var r = data.records[i];
30305                         var dup = this.store.getById(r.id);
30306                         if (dup && (dd != this.dragZone)) {
30307                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
30308                         } else {
30309                                 if (data.copy) {
30310                                         this.store.insert(insertAt++, r.copy());
30311                                 } else {
30312                                         data.source.isDirtyFlag = true;
30313                                         r.store.remove(r);
30314                                         this.store.insert(insertAt++, r);
30315                                 }
30316                                 this.isDirtyFlag = true;
30317                         }
30318                 }
30319                 this.dragZone.cachedTarget = null;
30320                 return true;
30321     },
30322
30323     removeDropIndicators : function(n){
30324                 if(n){
30325                         Roo.fly(n).removeClass([
30326                                 "x-view-drag-insert-above",
30327                                 "x-view-drag-insert-below"]);
30328                         this.lastInsertClass = "_noclass";
30329                 }
30330     },
30331
30332 /**
30333  *      Utility method. Add a delete option to the DDView's context menu.
30334  *      @param {String} imageUrl The URL of the "delete" icon image.
30335  */
30336         setDeletable: function(imageUrl) {
30337                 if (!this.singleSelect && !this.multiSelect) {
30338                         this.singleSelect = true;
30339                 }
30340                 var c = this.getContextMenu();
30341                 this.contextMenu.on("itemclick", function(item) {
30342                         switch (item.id) {
30343                                 case "delete":
30344                                         this.remove(this.getSelectedIndexes());
30345                                         break;
30346                         }
30347                 }, this);
30348                 this.contextMenu.add({
30349                         icon: imageUrl,
30350                         id: "delete",
30351                         text: 'Delete'
30352                 });
30353         },
30354         
30355 /**     Return the context menu for this DDView. */
30356         getContextMenu: function() {
30357                 if (!this.contextMenu) {
30358 //                      Create the View's context menu
30359                         this.contextMenu = new Roo.menu.Menu({
30360                                 id: this.id + "-contextmenu"
30361                         });
30362                         this.el.on("contextmenu", this.showContextMenu, this);
30363                 }
30364                 return this.contextMenu;
30365         },
30366         
30367         disableContextMenu: function() {
30368                 if (this.contextMenu) {
30369                         this.el.un("contextmenu", this.showContextMenu, this);
30370                 }
30371         },
30372
30373         showContextMenu: function(e, item) {
30374         item = this.findItemFromChild(e.getTarget());
30375                 if (item) {
30376                         e.stopEvent();
30377                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
30378                         this.contextMenu.showAt(e.getXY());
30379             }
30380     },
30381
30382 /**
30383  *      Remove {@link Roo.data.Record}s at the specified indices.
30384  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
30385  */
30386     remove: function(selectedIndices) {
30387                 selectedIndices = [].concat(selectedIndices);
30388                 for (var i = 0; i < selectedIndices.length; i++) {
30389                         var rec = this.store.getAt(selectedIndices[i]);
30390                         this.store.remove(rec);
30391                 }
30392     },
30393
30394 /**
30395  *      Double click fires the event, but also, if this is draggable, and there is only one other
30396  *      related DropZone, it transfers the selected node.
30397  */
30398     onDblClick : function(e){
30399         var item = this.findItemFromChild(e.getTarget());
30400         if(item){
30401             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
30402                 return false;
30403             }
30404             if (this.dragGroup) {
30405                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
30406                     while (targets.indexOf(this.dropZone) > -1) {
30407                             targets.remove(this.dropZone);
30408                                 }
30409                     if (targets.length == 1) {
30410                                         this.dragZone.cachedTarget = null;
30411                         var el = Roo.get(targets[0].getEl());
30412                         var box = el.getBox(true);
30413                         targets[0].onNodeDrop(el.dom, {
30414                                 target: el.dom,
30415                                 xy: [box.x, box.y + box.height - 1]
30416                         }, null, this.getDragData(e));
30417                     }
30418                 }
30419         }
30420     },
30421     
30422     handleSelection: function(e) {
30423                 this.dragZone.cachedTarget = null;
30424         var item = this.findItemFromChild(e.getTarget());
30425         if (!item) {
30426                 this.clearSelections(true);
30427                 return;
30428         }
30429                 if (item && (this.multiSelect || this.singleSelect)){
30430                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
30431                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
30432                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
30433                                 this.unselect(item);
30434                         } else {
30435                                 this.select(item, this.multiSelect && e.ctrlKey);
30436                                 this.lastSelection = item;
30437                         }
30438                 }
30439     },
30440
30441     onItemClick : function(item, index, e){
30442                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
30443                         return false;
30444                 }
30445                 return true;
30446     },
30447
30448     unselect : function(nodeInfo, suppressEvent){
30449                 var node = this.getNode(nodeInfo);
30450                 if(node && this.isSelected(node)){
30451                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
30452                                 Roo.fly(node).removeClass(this.selectedClass);
30453                                 this.selections.remove(node);
30454                                 if(!suppressEvent){
30455                                         this.fireEvent("selectionchange", this, this.selections);
30456                                 }
30457                         }
30458                 }
30459     }
30460 });
30461 /*
30462  * Based on:
30463  * Ext JS Library 1.1.1
30464  * Copyright(c) 2006-2007, Ext JS, LLC.
30465  *
30466  * Originally Released Under LGPL - original licence link has changed is not relivant.
30467  *
30468  * Fork - LGPL
30469  * <script type="text/javascript">
30470  */
30471  
30472 /**
30473  * @class Roo.LayoutManager
30474  * @extends Roo.util.Observable
30475  * Base class for layout managers.
30476  */
30477 Roo.LayoutManager = function(container, config){
30478     Roo.LayoutManager.superclass.constructor.call(this);
30479     this.el = Roo.get(container);
30480     // ie scrollbar fix
30481     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
30482         document.body.scroll = "no";
30483     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
30484         this.el.position('relative');
30485     }
30486     this.id = this.el.id;
30487     this.el.addClass("x-layout-container");
30488     /** false to disable window resize monitoring @type Boolean */
30489     this.monitorWindowResize = true;
30490     this.regions = {};
30491     this.addEvents({
30492         /**
30493          * @event layout
30494          * Fires when a layout is performed. 
30495          * @param {Roo.LayoutManager} this
30496          */
30497         "layout" : true,
30498         /**
30499          * @event regionresized
30500          * Fires when the user resizes a region. 
30501          * @param {Roo.LayoutRegion} region The resized region
30502          * @param {Number} newSize The new size (width for east/west, height for north/south)
30503          */
30504         "regionresized" : true,
30505         /**
30506          * @event regioncollapsed
30507          * Fires when a region is collapsed. 
30508          * @param {Roo.LayoutRegion} region The collapsed region
30509          */
30510         "regioncollapsed" : true,
30511         /**
30512          * @event regionexpanded
30513          * Fires when a region is expanded.  
30514          * @param {Roo.LayoutRegion} region The expanded region
30515          */
30516         "regionexpanded" : true
30517     });
30518     this.updating = false;
30519     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
30520 };
30521
30522 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
30523     /**
30524      * Returns true if this layout is currently being updated
30525      * @return {Boolean}
30526      */
30527     isUpdating : function(){
30528         return this.updating; 
30529     },
30530     
30531     /**
30532      * Suspend the LayoutManager from doing auto-layouts while
30533      * making multiple add or remove calls
30534      */
30535     beginUpdate : function(){
30536         this.updating = true;    
30537     },
30538     
30539     /**
30540      * Restore auto-layouts and optionally disable the manager from performing a layout
30541      * @param {Boolean} noLayout true to disable a layout update 
30542      */
30543     endUpdate : function(noLayout){
30544         this.updating = false;
30545         if(!noLayout){
30546             this.layout();
30547         }    
30548     },
30549     
30550     layout: function(){
30551         
30552     },
30553     
30554     onRegionResized : function(region, newSize){
30555         this.fireEvent("regionresized", region, newSize);
30556         this.layout();
30557     },
30558     
30559     onRegionCollapsed : function(region){
30560         this.fireEvent("regioncollapsed", region);
30561     },
30562     
30563     onRegionExpanded : function(region){
30564         this.fireEvent("regionexpanded", region);
30565     },
30566         
30567     /**
30568      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
30569      * performs box-model adjustments.
30570      * @return {Object} The size as an object {width: (the width), height: (the height)}
30571      */
30572     getViewSize : function(){
30573         var size;
30574         if(this.el.dom != document.body){
30575             size = this.el.getSize();
30576         }else{
30577             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
30578         }
30579         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
30580         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30581         return size;
30582     },
30583     
30584     /**
30585      * Returns the Element this layout is bound to.
30586      * @return {Roo.Element}
30587      */
30588     getEl : function(){
30589         return this.el;
30590     },
30591     
30592     /**
30593      * Returns the specified region.
30594      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
30595      * @return {Roo.LayoutRegion}
30596      */
30597     getRegion : function(target){
30598         return this.regions[target.toLowerCase()];
30599     },
30600     
30601     onWindowResize : function(){
30602         if(this.monitorWindowResize){
30603             this.layout();
30604         }
30605     }
30606 });/*
30607  * Based on:
30608  * Ext JS Library 1.1.1
30609  * Copyright(c) 2006-2007, Ext JS, LLC.
30610  *
30611  * Originally Released Under LGPL - original licence link has changed is not relivant.
30612  *
30613  * Fork - LGPL
30614  * <script type="text/javascript">
30615  */
30616 /**
30617  * @class Roo.BorderLayout
30618  * @extends Roo.LayoutManager
30619  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
30620  * please see: <br><br>
30621  * <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>
30622  * <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>
30623  * Example:
30624  <pre><code>
30625  var layout = new Roo.BorderLayout(document.body, {
30626     north: {
30627         initialSize: 25,
30628         titlebar: false
30629     },
30630     west: {
30631         split:true,
30632         initialSize: 200,
30633         minSize: 175,
30634         maxSize: 400,
30635         titlebar: true,
30636         collapsible: true
30637     },
30638     east: {
30639         split:true,
30640         initialSize: 202,
30641         minSize: 175,
30642         maxSize: 400,
30643         titlebar: true,
30644         collapsible: true
30645     },
30646     south: {
30647         split:true,
30648         initialSize: 100,
30649         minSize: 100,
30650         maxSize: 200,
30651         titlebar: true,
30652         collapsible: true
30653     },
30654     center: {
30655         titlebar: true,
30656         autoScroll:true,
30657         resizeTabs: true,
30658         minTabWidth: 50,
30659         preferredTabWidth: 150
30660     }
30661 });
30662
30663 // shorthand
30664 var CP = Roo.ContentPanel;
30665
30666 layout.beginUpdate();
30667 layout.add("north", new CP("north", "North"));
30668 layout.add("south", new CP("south", {title: "South", closable: true}));
30669 layout.add("west", new CP("west", {title: "West"}));
30670 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
30671 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
30672 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
30673 layout.getRegion("center").showPanel("center1");
30674 layout.endUpdate();
30675 </code></pre>
30676
30677 <b>The container the layout is rendered into can be either the body element or any other element.
30678 If it is not the body element, the container needs to either be an absolute positioned element,
30679 or you will need to add "position:relative" to the css of the container.  You will also need to specify
30680 the container size if it is not the body element.</b>
30681
30682 * @constructor
30683 * Create a new BorderLayout
30684 * @param {String/HTMLElement/Element} container The container this layout is bound to
30685 * @param {Object} config Configuration options
30686  */
30687 Roo.BorderLayout = function(container, config){
30688     config = config || {};
30689     Roo.BorderLayout.superclass.constructor.call(this, container, config);
30690     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
30691     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
30692         var target = this.factory.validRegions[i];
30693         if(config[target]){
30694             this.addRegion(target, config[target]);
30695         }
30696     }
30697 };
30698
30699 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
30700     /**
30701      * Creates and adds a new region if it doesn't already exist.
30702      * @param {String} target The target region key (north, south, east, west or center).
30703      * @param {Object} config The regions config object
30704      * @return {BorderLayoutRegion} The new region
30705      */
30706     addRegion : function(target, config){
30707         if(!this.regions[target]){
30708             var r = this.factory.create(target, this, config);
30709             this.bindRegion(target, r);
30710         }
30711         return this.regions[target];
30712     },
30713
30714     // private (kinda)
30715     bindRegion : function(name, r){
30716         this.regions[name] = r;
30717         r.on("visibilitychange", this.layout, this);
30718         r.on("paneladded", this.layout, this);
30719         r.on("panelremoved", this.layout, this);
30720         r.on("invalidated", this.layout, this);
30721         r.on("resized", this.onRegionResized, this);
30722         r.on("collapsed", this.onRegionCollapsed, this);
30723         r.on("expanded", this.onRegionExpanded, this);
30724     },
30725
30726     /**
30727      * Performs a layout update.
30728      */
30729     layout : function(){
30730         if(this.updating) return;
30731         var size = this.getViewSize();
30732         var w = size.width;
30733         var h = size.height;
30734         var centerW = w;
30735         var centerH = h;
30736         var centerY = 0;
30737         var centerX = 0;
30738         //var x = 0, y = 0;
30739
30740         var rs = this.regions;
30741         var north = rs["north"];
30742         var south = rs["south"]; 
30743         var west = rs["west"];
30744         var east = rs["east"];
30745         var center = rs["center"];
30746         //if(this.hideOnLayout){ // not supported anymore
30747             //c.el.setStyle("display", "none");
30748         //}
30749         if(north && north.isVisible()){
30750             var b = north.getBox();
30751             var m = north.getMargins();
30752             b.width = w - (m.left+m.right);
30753             b.x = m.left;
30754             b.y = m.top;
30755             centerY = b.height + b.y + m.bottom;
30756             centerH -= centerY;
30757             north.updateBox(this.safeBox(b));
30758         }
30759         if(south && south.isVisible()){
30760             var b = south.getBox();
30761             var m = south.getMargins();
30762             b.width = w - (m.left+m.right);
30763             b.x = m.left;
30764             var totalHeight = (b.height + m.top + m.bottom);
30765             b.y = h - totalHeight + m.top;
30766             centerH -= totalHeight;
30767             south.updateBox(this.safeBox(b));
30768         }
30769         if(west && west.isVisible()){
30770             var b = west.getBox();
30771             var m = west.getMargins();
30772             b.height = centerH - (m.top+m.bottom);
30773             b.x = m.left;
30774             b.y = centerY + m.top;
30775             var totalWidth = (b.width + m.left + m.right);
30776             centerX += totalWidth;
30777             centerW -= totalWidth;
30778             west.updateBox(this.safeBox(b));
30779         }
30780         if(east && east.isVisible()){
30781             var b = east.getBox();
30782             var m = east.getMargins();
30783             b.height = centerH - (m.top+m.bottom);
30784             var totalWidth = (b.width + m.left + m.right);
30785             b.x = w - totalWidth + m.left;
30786             b.y = centerY + m.top;
30787             centerW -= totalWidth;
30788             east.updateBox(this.safeBox(b));
30789         }
30790         if(center){
30791             var m = center.getMargins();
30792             var centerBox = {
30793                 x: centerX + m.left,
30794                 y: centerY + m.top,
30795                 width: centerW - (m.left+m.right),
30796                 height: centerH - (m.top+m.bottom)
30797             };
30798             //if(this.hideOnLayout){
30799                 //center.el.setStyle("display", "block");
30800             //}
30801             center.updateBox(this.safeBox(centerBox));
30802         }
30803         this.el.repaint();
30804         this.fireEvent("layout", this);
30805     },
30806
30807     // private
30808     safeBox : function(box){
30809         box.width = Math.max(0, box.width);
30810         box.height = Math.max(0, box.height);
30811         return box;
30812     },
30813
30814     /**
30815      * Adds a ContentPanel (or subclass) to this layout.
30816      * @param {String} target The target region key (north, south, east, west or center).
30817      * @param {Roo.ContentPanel} panel The panel to add
30818      * @return {Roo.ContentPanel} The added panel
30819      */
30820     add : function(target, panel){
30821          
30822         target = target.toLowerCase();
30823         return this.regions[target].add(panel);
30824     },
30825
30826     /**
30827      * Remove a ContentPanel (or subclass) to this layout.
30828      * @param {String} target The target region key (north, south, east, west or center).
30829      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
30830      * @return {Roo.ContentPanel} The removed panel
30831      */
30832     remove : function(target, panel){
30833         target = target.toLowerCase();
30834         return this.regions[target].remove(panel);
30835     },
30836
30837     /**
30838      * Searches all regions for a panel with the specified id
30839      * @param {String} panelId
30840      * @return {Roo.ContentPanel} The panel or null if it wasn't found
30841      */
30842     findPanel : function(panelId){
30843         var rs = this.regions;
30844         for(var target in rs){
30845             if(typeof rs[target] != "function"){
30846                 var p = rs[target].getPanel(panelId);
30847                 if(p){
30848                     return p;
30849                 }
30850             }
30851         }
30852         return null;
30853     },
30854
30855     /**
30856      * Searches all regions for a panel with the specified id and activates (shows) it.
30857      * @param {String/ContentPanel} panelId The panels id or the panel itself
30858      * @return {Roo.ContentPanel} The shown panel or null
30859      */
30860     showPanel : function(panelId) {
30861       var rs = this.regions;
30862       for(var target in rs){
30863          var r = rs[target];
30864          if(typeof r != "function"){
30865             if(r.hasPanel(panelId)){
30866                return r.showPanel(panelId);
30867             }
30868          }
30869       }
30870       return null;
30871    },
30872
30873    /**
30874      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
30875      * @param {Roo.state.Provider} provider (optional) An alternate state provider
30876      */
30877     restoreState : function(provider){
30878         if(!provider){
30879             provider = Roo.state.Manager;
30880         }
30881         var sm = new Roo.LayoutStateManager();
30882         sm.init(this, provider);
30883     },
30884
30885     /**
30886      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
30887      * object should contain properties for each region to add ContentPanels to, and each property's value should be
30888      * a valid ContentPanel config object.  Example:
30889      * <pre><code>
30890 // Create the main layout
30891 var layout = new Roo.BorderLayout('main-ct', {
30892     west: {
30893         split:true,
30894         minSize: 175,
30895         titlebar: true
30896     },
30897     center: {
30898         title:'Components'
30899     }
30900 }, 'main-ct');
30901
30902 // Create and add multiple ContentPanels at once via configs
30903 layout.batchAdd({
30904    west: {
30905        id: 'source-files',
30906        autoCreate:true,
30907        title:'Ext Source Files',
30908        autoScroll:true,
30909        fitToFrame:true
30910    },
30911    center : {
30912        el: cview,
30913        autoScroll:true,
30914        fitToFrame:true,
30915        toolbar: tb,
30916        resizeEl:'cbody'
30917    }
30918 });
30919 </code></pre>
30920      * @param {Object} regions An object containing ContentPanel configs by region name
30921      */
30922     batchAdd : function(regions){
30923         this.beginUpdate();
30924         for(var rname in regions){
30925             var lr = this.regions[rname];
30926             if(lr){
30927                 this.addTypedPanels(lr, regions[rname]);
30928             }
30929         }
30930         this.endUpdate();
30931     },
30932
30933     // private
30934     addTypedPanels : function(lr, ps){
30935         if(typeof ps == 'string'){
30936             lr.add(new Roo.ContentPanel(ps));
30937         }
30938         else if(ps instanceof Array){
30939             for(var i =0, len = ps.length; i < len; i++){
30940                 this.addTypedPanels(lr, ps[i]);
30941             }
30942         }
30943         else if(!ps.events){ // raw config?
30944             var el = ps.el;
30945             delete ps.el; // prevent conflict
30946             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
30947         }
30948         else {  // panel object assumed!
30949             lr.add(ps);
30950         }
30951     },
30952     /**
30953      * Adds a xtype elements to the layout.
30954      * <pre><code>
30955
30956 layout.addxtype({
30957        xtype : 'ContentPanel',
30958        region: 'west',
30959        items: [ .... ]
30960    }
30961 );
30962
30963 layout.addxtype({
30964         xtype : 'NestedLayoutPanel',
30965         region: 'west',
30966         layout: {
30967            center: { },
30968            west: { }   
30969         },
30970         items : [ ... list of content panels or nested layout panels.. ]
30971    }
30972 );
30973 </code></pre>
30974      * @param {Object} cfg Xtype definition of item to add.
30975      */
30976     addxtype : function(cfg)
30977     {
30978         // basically accepts a pannel...
30979         // can accept a layout region..!?!?
30980         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
30981         
30982         if (!cfg.xtype.match(/Panel$/)) {
30983             return false;
30984         }
30985         var ret = false;
30986         
30987         if (typeof(cfg.region) == 'undefined') {
30988             Roo.log("Failed to add Panel, region was not set");
30989             Roo.log(cfg);
30990             return false;
30991         }
30992         var region = cfg.region;
30993         delete cfg.region;
30994         
30995           
30996         var xitems = [];
30997         if (cfg.items) {
30998             xitems = cfg.items;
30999             delete cfg.items;
31000         }
31001         var nb = false;
31002         
31003         switch(cfg.xtype) 
31004         {
31005             case 'ContentPanel':  // ContentPanel (el, cfg)
31006             case 'ScrollPanel':  // ContentPanel (el, cfg)
31007                 if(cfg.autoCreate) {
31008                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31009                 } else {
31010                     var el = this.el.createChild();
31011                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31012                 }
31013                 
31014                 this.add(region, ret);
31015                 break;
31016             
31017             
31018             case 'TreePanel': // our new panel!
31019                 cfg.el = this.el.createChild();
31020                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31021                 this.add(region, ret);
31022                 break;
31023             
31024             case 'NestedLayoutPanel': 
31025                 // create a new Layout (which is  a Border Layout...
31026                 var el = this.el.createChild();
31027                 var clayout = cfg.layout;
31028                 delete cfg.layout;
31029                 clayout.items   = clayout.items  || [];
31030                 // replace this exitems with the clayout ones..
31031                 xitems = clayout.items;
31032                  
31033                 
31034                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31035                     cfg.background = false;
31036                 }
31037                 var layout = new Roo.BorderLayout(el, clayout);
31038                 
31039                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
31040                 //console.log('adding nested layout panel '  + cfg.toSource());
31041                 this.add(region, ret);
31042                 nb = {}; /// find first...
31043                 break;
31044                 
31045             case 'GridPanel': 
31046             
31047                 // needs grid and region
31048                 
31049                 //var el = this.getRegion(region).el.createChild();
31050                 var el = this.el.createChild();
31051                 // create the grid first...
31052                 
31053                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
31054                 delete cfg.grid;
31055                 if (region == 'center' && this.active ) {
31056                     cfg.background = false;
31057                 }
31058                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
31059                 
31060                 this.add(region, ret);
31061                 if (cfg.background) {
31062                     ret.on('activate', function(gp) {
31063                         if (!gp.grid.rendered) {
31064                             gp.grid.render();
31065                         }
31066                     });
31067                 } else {
31068                     grid.render();
31069                 }
31070                 break;
31071            
31072                
31073                 
31074                 
31075             default: 
31076                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
31077                 return null;
31078              // GridPanel (grid, cfg)
31079             
31080         }
31081         this.beginUpdate();
31082         // add children..
31083         var region = '';
31084         var abn = {};
31085         Roo.each(xitems, function(i)  {
31086             region = nb && i.region ? i.region : false;
31087             
31088             var add = ret.addxtype(i);
31089            
31090             if (region) {
31091                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31092                 if (!i.background) {
31093                     abn[region] = nb[region] ;
31094                 }
31095             }
31096             
31097         });
31098         this.endUpdate();
31099
31100         // make the last non-background panel active..
31101         //if (nb) { Roo.log(abn); }
31102         if (nb) {
31103             
31104             for(var r in abn) {
31105                 region = this.getRegion(r);
31106                 if (region) {
31107                     // tried using nb[r], but it does not work..
31108                      
31109                     region.showPanel(abn[r]);
31110                    
31111                 }
31112             }
31113         }
31114         return ret;
31115         
31116     }
31117 });
31118
31119 /**
31120  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
31121  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
31122  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
31123  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
31124  * <pre><code>
31125 // shorthand
31126 var CP = Roo.ContentPanel;
31127
31128 var layout = Roo.BorderLayout.create({
31129     north: {
31130         initialSize: 25,
31131         titlebar: false,
31132         panels: [new CP("north", "North")]
31133     },
31134     west: {
31135         split:true,
31136         initialSize: 200,
31137         minSize: 175,
31138         maxSize: 400,
31139         titlebar: true,
31140         collapsible: true,
31141         panels: [new CP("west", {title: "West"})]
31142     },
31143     east: {
31144         split:true,
31145         initialSize: 202,
31146         minSize: 175,
31147         maxSize: 400,
31148         titlebar: true,
31149         collapsible: true,
31150         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
31151     },
31152     south: {
31153         split:true,
31154         initialSize: 100,
31155         minSize: 100,
31156         maxSize: 200,
31157         titlebar: true,
31158         collapsible: true,
31159         panels: [new CP("south", {title: "South", closable: true})]
31160     },
31161     center: {
31162         titlebar: true,
31163         autoScroll:true,
31164         resizeTabs: true,
31165         minTabWidth: 50,
31166         preferredTabWidth: 150,
31167         panels: [
31168             new CP("center1", {title: "Close Me", closable: true}),
31169             new CP("center2", {title: "Center Panel", closable: false})
31170         ]
31171     }
31172 }, document.body);
31173
31174 layout.getRegion("center").showPanel("center1");
31175 </code></pre>
31176  * @param config
31177  * @param targetEl
31178  */
31179 Roo.BorderLayout.create = function(config, targetEl){
31180     var layout = new Roo.BorderLayout(targetEl || document.body, config);
31181     layout.beginUpdate();
31182     var regions = Roo.BorderLayout.RegionFactory.validRegions;
31183     for(var j = 0, jlen = regions.length; j < jlen; j++){
31184         var lr = regions[j];
31185         if(layout.regions[lr] && config[lr].panels){
31186             var r = layout.regions[lr];
31187             var ps = config[lr].panels;
31188             layout.addTypedPanels(r, ps);
31189         }
31190     }
31191     layout.endUpdate();
31192     return layout;
31193 };
31194
31195 // private
31196 Roo.BorderLayout.RegionFactory = {
31197     // private
31198     validRegions : ["north","south","east","west","center"],
31199
31200     // private
31201     create : function(target, mgr, config){
31202         target = target.toLowerCase();
31203         if(config.lightweight || config.basic){
31204             return new Roo.BasicLayoutRegion(mgr, config, target);
31205         }
31206         switch(target){
31207             case "north":
31208                 return new Roo.NorthLayoutRegion(mgr, config);
31209             case "south":
31210                 return new Roo.SouthLayoutRegion(mgr, config);
31211             case "east":
31212                 return new Roo.EastLayoutRegion(mgr, config);
31213             case "west":
31214                 return new Roo.WestLayoutRegion(mgr, config);
31215             case "center":
31216                 return new Roo.CenterLayoutRegion(mgr, config);
31217         }
31218         throw 'Layout region "'+target+'" not supported.';
31219     }
31220 };/*
31221  * Based on:
31222  * Ext JS Library 1.1.1
31223  * Copyright(c) 2006-2007, Ext JS, LLC.
31224  *
31225  * Originally Released Under LGPL - original licence link has changed is not relivant.
31226  *
31227  * Fork - LGPL
31228  * <script type="text/javascript">
31229  */
31230  
31231 /**
31232  * @class Roo.BasicLayoutRegion
31233  * @extends Roo.util.Observable
31234  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31235  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31236  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31237  */
31238 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
31239     this.mgr = mgr;
31240     this.position  = pos;
31241     this.events = {
31242         /**
31243          * @scope Roo.BasicLayoutRegion
31244          */
31245         
31246         /**
31247          * @event beforeremove
31248          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31249          * @param {Roo.LayoutRegion} this
31250          * @param {Roo.ContentPanel} panel The panel
31251          * @param {Object} e The cancel event object
31252          */
31253         "beforeremove" : true,
31254         /**
31255          * @event invalidated
31256          * Fires when the layout for this region is changed.
31257          * @param {Roo.LayoutRegion} this
31258          */
31259         "invalidated" : true,
31260         /**
31261          * @event visibilitychange
31262          * Fires when this region is shown or hidden 
31263          * @param {Roo.LayoutRegion} this
31264          * @param {Boolean} visibility true or false
31265          */
31266         "visibilitychange" : true,
31267         /**
31268          * @event paneladded
31269          * Fires when a panel is added. 
31270          * @param {Roo.LayoutRegion} this
31271          * @param {Roo.ContentPanel} panel The panel
31272          */
31273         "paneladded" : true,
31274         /**
31275          * @event panelremoved
31276          * Fires when a panel is removed. 
31277          * @param {Roo.LayoutRegion} this
31278          * @param {Roo.ContentPanel} panel The panel
31279          */
31280         "panelremoved" : true,
31281         /**
31282          * @event collapsed
31283          * Fires when this region is collapsed.
31284          * @param {Roo.LayoutRegion} this
31285          */
31286         "collapsed" : true,
31287         /**
31288          * @event expanded
31289          * Fires when this region is expanded.
31290          * @param {Roo.LayoutRegion} this
31291          */
31292         "expanded" : true,
31293         /**
31294          * @event slideshow
31295          * Fires when this region is slid into view.
31296          * @param {Roo.LayoutRegion} this
31297          */
31298         "slideshow" : true,
31299         /**
31300          * @event slidehide
31301          * Fires when this region slides out of view. 
31302          * @param {Roo.LayoutRegion} this
31303          */
31304         "slidehide" : true,
31305         /**
31306          * @event panelactivated
31307          * Fires when a panel is activated. 
31308          * @param {Roo.LayoutRegion} this
31309          * @param {Roo.ContentPanel} panel The activated panel
31310          */
31311         "panelactivated" : true,
31312         /**
31313          * @event resized
31314          * Fires when the user resizes this region. 
31315          * @param {Roo.LayoutRegion} this
31316          * @param {Number} newSize The new size (width for east/west, height for north/south)
31317          */
31318         "resized" : true
31319     };
31320     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31321     this.panels = new Roo.util.MixedCollection();
31322     this.panels.getKey = this.getPanelId.createDelegate(this);
31323     this.box = null;
31324     this.activePanel = null;
31325     // ensure listeners are added...
31326     
31327     if (config.listeners || config.events) {
31328         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
31329             listeners : config.listeners || {},
31330             events : config.events || {}
31331         });
31332     }
31333     
31334     if(skipConfig !== true){
31335         this.applyConfig(config);
31336     }
31337 };
31338
31339 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
31340     getPanelId : function(p){
31341         return p.getId();
31342     },
31343     
31344     applyConfig : function(config){
31345         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31346         this.config = config;
31347         
31348     },
31349     
31350     /**
31351      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31352      * the width, for horizontal (north, south) the height.
31353      * @param {Number} newSize The new width or height
31354      */
31355     resizeTo : function(newSize){
31356         var el = this.el ? this.el :
31357                  (this.activePanel ? this.activePanel.getEl() : null);
31358         if(el){
31359             switch(this.position){
31360                 case "east":
31361                 case "west":
31362                     el.setWidth(newSize);
31363                     this.fireEvent("resized", this, newSize);
31364                 break;
31365                 case "north":
31366                 case "south":
31367                     el.setHeight(newSize);
31368                     this.fireEvent("resized", this, newSize);
31369                 break;                
31370             }
31371         }
31372     },
31373     
31374     getBox : function(){
31375         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31376     },
31377     
31378     getMargins : function(){
31379         return this.margins;
31380     },
31381     
31382     updateBox : function(box){
31383         this.box = box;
31384         var el = this.activePanel.getEl();
31385         el.dom.style.left = box.x + "px";
31386         el.dom.style.top = box.y + "px";
31387         this.activePanel.setSize(box.width, box.height);
31388     },
31389     
31390     /**
31391      * Returns the container element for this region.
31392      * @return {Roo.Element}
31393      */
31394     getEl : function(){
31395         return this.activePanel;
31396     },
31397     
31398     /**
31399      * Returns true if this region is currently visible.
31400      * @return {Boolean}
31401      */
31402     isVisible : function(){
31403         return this.activePanel ? true : false;
31404     },
31405     
31406     setActivePanel : function(panel){
31407         panel = this.getPanel(panel);
31408         if(this.activePanel && this.activePanel != panel){
31409             this.activePanel.setActiveState(false);
31410             this.activePanel.getEl().setLeftTop(-10000,-10000);
31411         }
31412         this.activePanel = panel;
31413         panel.setActiveState(true);
31414         if(this.box){
31415             panel.setSize(this.box.width, this.box.height);
31416         }
31417         this.fireEvent("panelactivated", this, panel);
31418         this.fireEvent("invalidated");
31419     },
31420     
31421     /**
31422      * Show the specified panel.
31423      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31424      * @return {Roo.ContentPanel} The shown panel or null
31425      */
31426     showPanel : function(panel){
31427         if(panel = this.getPanel(panel)){
31428             this.setActivePanel(panel);
31429         }
31430         return panel;
31431     },
31432     
31433     /**
31434      * Get the active panel for this region.
31435      * @return {Roo.ContentPanel} The active panel or null
31436      */
31437     getActivePanel : function(){
31438         return this.activePanel;
31439     },
31440     
31441     /**
31442      * Add the passed ContentPanel(s)
31443      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31444      * @return {Roo.ContentPanel} The panel added (if only one was added)
31445      */
31446     add : function(panel){
31447         if(arguments.length > 1){
31448             for(var i = 0, len = arguments.length; i < len; i++) {
31449                 this.add(arguments[i]);
31450             }
31451             return null;
31452         }
31453         if(this.hasPanel(panel)){
31454             this.showPanel(panel);
31455             return panel;
31456         }
31457         var el = panel.getEl();
31458         if(el.dom.parentNode != this.mgr.el.dom){
31459             this.mgr.el.dom.appendChild(el.dom);
31460         }
31461         if(panel.setRegion){
31462             panel.setRegion(this);
31463         }
31464         this.panels.add(panel);
31465         el.setStyle("position", "absolute");
31466         if(!panel.background){
31467             this.setActivePanel(panel);
31468             if(this.config.initialSize && this.panels.getCount()==1){
31469                 this.resizeTo(this.config.initialSize);
31470             }
31471         }
31472         this.fireEvent("paneladded", this, panel);
31473         return panel;
31474     },
31475     
31476     /**
31477      * Returns true if the panel is in this region.
31478      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31479      * @return {Boolean}
31480      */
31481     hasPanel : function(panel){
31482         if(typeof panel == "object"){ // must be panel obj
31483             panel = panel.getId();
31484         }
31485         return this.getPanel(panel) ? true : false;
31486     },
31487     
31488     /**
31489      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31490      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31491      * @param {Boolean} preservePanel Overrides the config preservePanel option
31492      * @return {Roo.ContentPanel} The panel that was removed
31493      */
31494     remove : function(panel, preservePanel){
31495         panel = this.getPanel(panel);
31496         if(!panel){
31497             return null;
31498         }
31499         var e = {};
31500         this.fireEvent("beforeremove", this, panel, e);
31501         if(e.cancel === true){
31502             return null;
31503         }
31504         var panelId = panel.getId();
31505         this.panels.removeKey(panelId);
31506         return panel;
31507     },
31508     
31509     /**
31510      * Returns the panel specified or null if it's not in this region.
31511      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31512      * @return {Roo.ContentPanel}
31513      */
31514     getPanel : function(id){
31515         if(typeof id == "object"){ // must be panel obj
31516             return id;
31517         }
31518         return this.panels.get(id);
31519     },
31520     
31521     /**
31522      * Returns this regions position (north/south/east/west/center).
31523      * @return {String} 
31524      */
31525     getPosition: function(){
31526         return this.position;    
31527     }
31528 });/*
31529  * Based on:
31530  * Ext JS Library 1.1.1
31531  * Copyright(c) 2006-2007, Ext JS, LLC.
31532  *
31533  * Originally Released Under LGPL - original licence link has changed is not relivant.
31534  *
31535  * Fork - LGPL
31536  * <script type="text/javascript">
31537  */
31538  
31539 /**
31540  * @class Roo.LayoutRegion
31541  * @extends Roo.BasicLayoutRegion
31542  * This class represents a region in a layout manager.
31543  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
31544  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
31545  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
31546  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31547  * @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})
31548  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
31549  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
31550  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
31551  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
31552  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
31553  * @cfg {String}    title           The title for the region (overrides panel titles)
31554  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
31555  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31556  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
31557  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31558  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
31559  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31560  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
31561  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
31562  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
31563  * @cfg {Boolean}   showPin         True to show a pin button
31564  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
31565  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
31566  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
31567  * @cfg {Number}    width           For East/West panels
31568  * @cfg {Number}    height          For North/South panels
31569  * @cfg {Boolean}   split           To show the splitter
31570  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
31571  */
31572 Roo.LayoutRegion = function(mgr, config, pos){
31573     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
31574     var dh = Roo.DomHelper;
31575     /** This region's container element 
31576     * @type Roo.Element */
31577     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
31578     /** This region's title element 
31579     * @type Roo.Element */
31580
31581     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
31582         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
31583         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
31584     ]}, true);
31585     this.titleEl.enableDisplayMode();
31586     /** This region's title text element 
31587     * @type HTMLElement */
31588     this.titleTextEl = this.titleEl.dom.firstChild;
31589     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
31590     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
31591     this.closeBtn.enableDisplayMode();
31592     this.closeBtn.on("click", this.closeClicked, this);
31593     this.closeBtn.hide();
31594
31595     this.createBody(config);
31596     this.visible = true;
31597     this.collapsed = false;
31598
31599     if(config.hideWhenEmpty){
31600         this.hide();
31601         this.on("paneladded", this.validateVisibility, this);
31602         this.on("panelremoved", this.validateVisibility, this);
31603     }
31604     this.applyConfig(config);
31605 };
31606
31607 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
31608
31609     createBody : function(){
31610         /** This region's body element 
31611         * @type Roo.Element */
31612         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
31613     },
31614
31615     applyConfig : function(c){
31616         if(c.collapsible && this.position != "center" && !this.collapsedEl){
31617             var dh = Roo.DomHelper;
31618             if(c.titlebar !== false){
31619                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
31620                 this.collapseBtn.on("click", this.collapse, this);
31621                 this.collapseBtn.enableDisplayMode();
31622
31623                 if(c.showPin === true || this.showPin){
31624                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
31625                     this.stickBtn.enableDisplayMode();
31626                     this.stickBtn.on("click", this.expand, this);
31627                     this.stickBtn.hide();
31628                 }
31629             }
31630             /** This region's collapsed element
31631             * @type Roo.Element */
31632             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
31633                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
31634             ]}, true);
31635             if(c.floatable !== false){
31636                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
31637                this.collapsedEl.on("click", this.collapseClick, this);
31638             }
31639
31640             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
31641                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
31642                    id: "message", unselectable: "on", style:{"float":"left"}});
31643                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
31644              }
31645             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
31646             this.expandBtn.on("click", this.expand, this);
31647         }
31648         if(this.collapseBtn){
31649             this.collapseBtn.setVisible(c.collapsible == true);
31650         }
31651         this.cmargins = c.cmargins || this.cmargins ||
31652                          (this.position == "west" || this.position == "east" ?
31653                              {top: 0, left: 2, right:2, bottom: 0} :
31654                              {top: 2, left: 0, right:0, bottom: 2});
31655         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31656         this.bottomTabs = c.tabPosition != "top";
31657         this.autoScroll = c.autoScroll || false;
31658         if(this.autoScroll){
31659             this.bodyEl.setStyle("overflow", "auto");
31660         }else{
31661             this.bodyEl.setStyle("overflow", "hidden");
31662         }
31663         //if(c.titlebar !== false){
31664             if((!c.titlebar && !c.title) || c.titlebar === false){
31665                 this.titleEl.hide();
31666             }else{
31667                 this.titleEl.show();
31668                 if(c.title){
31669                     this.titleTextEl.innerHTML = c.title;
31670                 }
31671             }
31672         //}
31673         this.duration = c.duration || .30;
31674         this.slideDuration = c.slideDuration || .45;
31675         this.config = c;
31676         if(c.collapsed){
31677             this.collapse(true);
31678         }
31679         if(c.hidden){
31680             this.hide();
31681         }
31682     },
31683     /**
31684      * Returns true if this region is currently visible.
31685      * @return {Boolean}
31686      */
31687     isVisible : function(){
31688         return this.visible;
31689     },
31690
31691     /**
31692      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
31693      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
31694      */
31695     setCollapsedTitle : function(title){
31696         title = title || "&#160;";
31697         if(this.collapsedTitleTextEl){
31698             this.collapsedTitleTextEl.innerHTML = title;
31699         }
31700     },
31701
31702     getBox : function(){
31703         var b;
31704         if(!this.collapsed){
31705             b = this.el.getBox(false, true);
31706         }else{
31707             b = this.collapsedEl.getBox(false, true);
31708         }
31709         return b;
31710     },
31711
31712     getMargins : function(){
31713         return this.collapsed ? this.cmargins : this.margins;
31714     },
31715
31716     highlight : function(){
31717         this.el.addClass("x-layout-panel-dragover");
31718     },
31719
31720     unhighlight : function(){
31721         this.el.removeClass("x-layout-panel-dragover");
31722     },
31723
31724     updateBox : function(box){
31725         this.box = box;
31726         if(!this.collapsed){
31727             this.el.dom.style.left = box.x + "px";
31728             this.el.dom.style.top = box.y + "px";
31729             this.updateBody(box.width, box.height);
31730         }else{
31731             this.collapsedEl.dom.style.left = box.x + "px";
31732             this.collapsedEl.dom.style.top = box.y + "px";
31733             this.collapsedEl.setSize(box.width, box.height);
31734         }
31735         if(this.tabs){
31736             this.tabs.autoSizeTabs();
31737         }
31738     },
31739
31740     updateBody : function(w, h){
31741         if(w !== null){
31742             this.el.setWidth(w);
31743             w -= this.el.getBorderWidth("rl");
31744             if(this.config.adjustments){
31745                 w += this.config.adjustments[0];
31746             }
31747         }
31748         if(h !== null){
31749             this.el.setHeight(h);
31750             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
31751             h -= this.el.getBorderWidth("tb");
31752             if(this.config.adjustments){
31753                 h += this.config.adjustments[1];
31754             }
31755             this.bodyEl.setHeight(h);
31756             if(this.tabs){
31757                 h = this.tabs.syncHeight(h);
31758             }
31759         }
31760         if(this.panelSize){
31761             w = w !== null ? w : this.panelSize.width;
31762             h = h !== null ? h : this.panelSize.height;
31763         }
31764         if(this.activePanel){
31765             var el = this.activePanel.getEl();
31766             w = w !== null ? w : el.getWidth();
31767             h = h !== null ? h : el.getHeight();
31768             this.panelSize = {width: w, height: h};
31769             this.activePanel.setSize(w, h);
31770         }
31771         if(Roo.isIE && this.tabs){
31772             this.tabs.el.repaint();
31773         }
31774     },
31775
31776     /**
31777      * Returns the container element for this region.
31778      * @return {Roo.Element}
31779      */
31780     getEl : function(){
31781         return this.el;
31782     },
31783
31784     /**
31785      * Hides this region.
31786      */
31787     hide : function(){
31788         if(!this.collapsed){
31789             this.el.dom.style.left = "-2000px";
31790             this.el.hide();
31791         }else{
31792             this.collapsedEl.dom.style.left = "-2000px";
31793             this.collapsedEl.hide();
31794         }
31795         this.visible = false;
31796         this.fireEvent("visibilitychange", this, false);
31797     },
31798
31799     /**
31800      * Shows this region if it was previously hidden.
31801      */
31802     show : function(){
31803         if(!this.collapsed){
31804             this.el.show();
31805         }else{
31806             this.collapsedEl.show();
31807         }
31808         this.visible = true;
31809         this.fireEvent("visibilitychange", this, true);
31810     },
31811
31812     closeClicked : function(){
31813         if(this.activePanel){
31814             this.remove(this.activePanel);
31815         }
31816     },
31817
31818     collapseClick : function(e){
31819         if(this.isSlid){
31820            e.stopPropagation();
31821            this.slideIn();
31822         }else{
31823            e.stopPropagation();
31824            this.slideOut();
31825         }
31826     },
31827
31828     /**
31829      * Collapses this region.
31830      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
31831      */
31832     collapse : function(skipAnim){
31833         if(this.collapsed) return;
31834         this.collapsed = true;
31835         if(this.split){
31836             this.split.el.hide();
31837         }
31838         if(this.config.animate && skipAnim !== true){
31839             this.fireEvent("invalidated", this);
31840             this.animateCollapse();
31841         }else{
31842             this.el.setLocation(-20000,-20000);
31843             this.el.hide();
31844             this.collapsedEl.show();
31845             this.fireEvent("collapsed", this);
31846             this.fireEvent("invalidated", this);
31847         }
31848     },
31849
31850     animateCollapse : function(){
31851         // overridden
31852     },
31853
31854     /**
31855      * Expands this region if it was previously collapsed.
31856      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
31857      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
31858      */
31859     expand : function(e, skipAnim){
31860         if(e) e.stopPropagation();
31861         if(!this.collapsed || this.el.hasActiveFx()) return;
31862         if(this.isSlid){
31863             this.afterSlideIn();
31864             skipAnim = true;
31865         }
31866         this.collapsed = false;
31867         if(this.config.animate && skipAnim !== true){
31868             this.animateExpand();
31869         }else{
31870             this.el.show();
31871             if(this.split){
31872                 this.split.el.show();
31873             }
31874             this.collapsedEl.setLocation(-2000,-2000);
31875             this.collapsedEl.hide();
31876             this.fireEvent("invalidated", this);
31877             this.fireEvent("expanded", this);
31878         }
31879     },
31880
31881     animateExpand : function(){
31882         // overridden
31883     },
31884
31885     initTabs : function()
31886     {
31887         this.bodyEl.setStyle("overflow", "hidden");
31888         var ts = new Roo.TabPanel(
31889                 this.bodyEl.dom,
31890                 {
31891                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
31892                     disableTooltips: this.config.disableTabTips,
31893                     toolbar : this.config.toolbar
31894                 }
31895         );
31896         if(this.config.hideTabs){
31897             ts.stripWrap.setDisplayed(false);
31898         }
31899         this.tabs = ts;
31900         ts.resizeTabs = this.config.resizeTabs === true;
31901         ts.minTabWidth = this.config.minTabWidth || 40;
31902         ts.maxTabWidth = this.config.maxTabWidth || 250;
31903         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
31904         ts.monitorResize = false;
31905         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31906         ts.bodyEl.addClass('x-layout-tabs-body');
31907         this.panels.each(this.initPanelAsTab, this);
31908     },
31909
31910     initPanelAsTab : function(panel){
31911         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
31912                     this.config.closeOnTab && panel.isClosable());
31913         if(panel.tabTip !== undefined){
31914             ti.setTooltip(panel.tabTip);
31915         }
31916         ti.on("activate", function(){
31917               this.setActivePanel(panel);
31918         }, this);
31919         if(this.config.closeOnTab){
31920             ti.on("beforeclose", function(t, e){
31921                 e.cancel = true;
31922                 this.remove(panel);
31923             }, this);
31924         }
31925         return ti;
31926     },
31927
31928     updatePanelTitle : function(panel, title){
31929         if(this.activePanel == panel){
31930             this.updateTitle(title);
31931         }
31932         if(this.tabs){
31933             var ti = this.tabs.getTab(panel.getEl().id);
31934             ti.setText(title);
31935             if(panel.tabTip !== undefined){
31936                 ti.setTooltip(panel.tabTip);
31937             }
31938         }
31939     },
31940
31941     updateTitle : function(title){
31942         if(this.titleTextEl && !this.config.title){
31943             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
31944         }
31945     },
31946
31947     setActivePanel : function(panel){
31948         panel = this.getPanel(panel);
31949         if(this.activePanel && this.activePanel != panel){
31950             this.activePanel.setActiveState(false);
31951         }
31952         this.activePanel = panel;
31953         panel.setActiveState(true);
31954         if(this.panelSize){
31955             panel.setSize(this.panelSize.width, this.panelSize.height);
31956         }
31957         if(this.closeBtn){
31958             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
31959         }
31960         this.updateTitle(panel.getTitle());
31961         if(this.tabs){
31962             this.fireEvent("invalidated", this);
31963         }
31964         this.fireEvent("panelactivated", this, panel);
31965     },
31966
31967     /**
31968      * Shows the specified panel.
31969      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
31970      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
31971      */
31972     showPanel : function(panel){
31973         if(panel = this.getPanel(panel)){
31974             if(this.tabs){
31975                 var tab = this.tabs.getTab(panel.getEl().id);
31976                 if(tab.isHidden()){
31977                     this.tabs.unhideTab(tab.id);
31978                 }
31979                 tab.activate();
31980             }else{
31981                 this.setActivePanel(panel);
31982             }
31983         }
31984         return panel;
31985     },
31986
31987     /**
31988      * Get the active panel for this region.
31989      * @return {Roo.ContentPanel} The active panel or null
31990      */
31991     getActivePanel : function(){
31992         return this.activePanel;
31993     },
31994
31995     validateVisibility : function(){
31996         if(this.panels.getCount() < 1){
31997             this.updateTitle("&#160;");
31998             this.closeBtn.hide();
31999             this.hide();
32000         }else{
32001             if(!this.isVisible()){
32002                 this.show();
32003             }
32004         }
32005     },
32006
32007     /**
32008      * Adds the passed ContentPanel(s) to this region.
32009      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32010      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32011      */
32012     add : function(panel){
32013         if(arguments.length > 1){
32014             for(var i = 0, len = arguments.length; i < len; i++) {
32015                 this.add(arguments[i]);
32016             }
32017             return null;
32018         }
32019         if(this.hasPanel(panel)){
32020             this.showPanel(panel);
32021             return panel;
32022         }
32023         panel.setRegion(this);
32024         this.panels.add(panel);
32025         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32026             this.bodyEl.dom.appendChild(panel.getEl().dom);
32027             if(panel.background !== true){
32028                 this.setActivePanel(panel);
32029             }
32030             this.fireEvent("paneladded", this, panel);
32031             return panel;
32032         }
32033         if(!this.tabs){
32034             this.initTabs();
32035         }else{
32036             this.initPanelAsTab(panel);
32037         }
32038         if(panel.background !== true){
32039             this.tabs.activate(panel.getEl().id);
32040         }
32041         this.fireEvent("paneladded", this, panel);
32042         return panel;
32043     },
32044
32045     /**
32046      * Hides the tab for the specified panel.
32047      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32048      */
32049     hidePanel : function(panel){
32050         if(this.tabs && (panel = this.getPanel(panel))){
32051             this.tabs.hideTab(panel.getEl().id);
32052         }
32053     },
32054
32055     /**
32056      * Unhides the tab for a previously hidden panel.
32057      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32058      */
32059     unhidePanel : function(panel){
32060         if(this.tabs && (panel = this.getPanel(panel))){
32061             this.tabs.unhideTab(panel.getEl().id);
32062         }
32063     },
32064
32065     clearPanels : function(){
32066         while(this.panels.getCount() > 0){
32067              this.remove(this.panels.first());
32068         }
32069     },
32070
32071     /**
32072      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32073      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32074      * @param {Boolean} preservePanel Overrides the config preservePanel option
32075      * @return {Roo.ContentPanel} The panel that was removed
32076      */
32077     remove : function(panel, preservePanel){
32078         panel = this.getPanel(panel);
32079         if(!panel){
32080             return null;
32081         }
32082         var e = {};
32083         this.fireEvent("beforeremove", this, panel, e);
32084         if(e.cancel === true){
32085             return null;
32086         }
32087         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32088         var panelId = panel.getId();
32089         this.panels.removeKey(panelId);
32090         if(preservePanel){
32091             document.body.appendChild(panel.getEl().dom);
32092         }
32093         if(this.tabs){
32094             this.tabs.removeTab(panel.getEl().id);
32095         }else if (!preservePanel){
32096             this.bodyEl.dom.removeChild(panel.getEl().dom);
32097         }
32098         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32099             var p = this.panels.first();
32100             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32101             tempEl.appendChild(p.getEl().dom);
32102             this.bodyEl.update("");
32103             this.bodyEl.dom.appendChild(p.getEl().dom);
32104             tempEl = null;
32105             this.updateTitle(p.getTitle());
32106             this.tabs = null;
32107             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32108             this.setActivePanel(p);
32109         }
32110         panel.setRegion(null);
32111         if(this.activePanel == panel){
32112             this.activePanel = null;
32113         }
32114         if(this.config.autoDestroy !== false && preservePanel !== true){
32115             try{panel.destroy();}catch(e){}
32116         }
32117         this.fireEvent("panelremoved", this, panel);
32118         return panel;
32119     },
32120
32121     /**
32122      * Returns the TabPanel component used by this region
32123      * @return {Roo.TabPanel}
32124      */
32125     getTabs : function(){
32126         return this.tabs;
32127     },
32128
32129     createTool : function(parentEl, className){
32130         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
32131             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
32132         btn.addClassOnOver("x-layout-tools-button-over");
32133         return btn;
32134     }
32135 });/*
32136  * Based on:
32137  * Ext JS Library 1.1.1
32138  * Copyright(c) 2006-2007, Ext JS, LLC.
32139  *
32140  * Originally Released Under LGPL - original licence link has changed is not relivant.
32141  *
32142  * Fork - LGPL
32143  * <script type="text/javascript">
32144  */
32145  
32146
32147
32148 /**
32149  * @class Roo.SplitLayoutRegion
32150  * @extends Roo.LayoutRegion
32151  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32152  */
32153 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
32154     this.cursor = cursor;
32155     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
32156 };
32157
32158 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
32159     splitTip : "Drag to resize.",
32160     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32161     useSplitTips : false,
32162
32163     applyConfig : function(config){
32164         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
32165         if(config.split){
32166             if(!this.split){
32167                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
32168                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
32169                 /** The SplitBar for this region 
32170                 * @type Roo.SplitBar */
32171                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
32172                 this.split.on("moved", this.onSplitMove, this);
32173                 this.split.useShim = config.useShim === true;
32174                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32175                 if(this.useSplitTips){
32176                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32177                 }
32178                 if(config.collapsible){
32179                     this.split.el.on("dblclick", this.collapse,  this);
32180                 }
32181             }
32182             if(typeof config.minSize != "undefined"){
32183                 this.split.minSize = config.minSize;
32184             }
32185             if(typeof config.maxSize != "undefined"){
32186                 this.split.maxSize = config.maxSize;
32187             }
32188             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32189                 this.hideSplitter();
32190             }
32191         }
32192     },
32193
32194     getHMaxSize : function(){
32195          var cmax = this.config.maxSize || 10000;
32196          var center = this.mgr.getRegion("center");
32197          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32198     },
32199
32200     getVMaxSize : function(){
32201          var cmax = this.config.maxSize || 10000;
32202          var center = this.mgr.getRegion("center");
32203          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32204     },
32205
32206     onSplitMove : function(split, newSize){
32207         this.fireEvent("resized", this, newSize);
32208     },
32209     
32210     /** 
32211      * Returns the {@link Roo.SplitBar} for this region.
32212      * @return {Roo.SplitBar}
32213      */
32214     getSplitBar : function(){
32215         return this.split;
32216     },
32217     
32218     hide : function(){
32219         this.hideSplitter();
32220         Roo.SplitLayoutRegion.superclass.hide.call(this);
32221     },
32222
32223     hideSplitter : function(){
32224         if(this.split){
32225             this.split.el.setLocation(-2000,-2000);
32226             this.split.el.hide();
32227         }
32228     },
32229
32230     show : function(){
32231         if(this.split){
32232             this.split.el.show();
32233         }
32234         Roo.SplitLayoutRegion.superclass.show.call(this);
32235     },
32236     
32237     beforeSlide: function(){
32238         if(Roo.isGecko){// firefox overflow auto bug workaround
32239             this.bodyEl.clip();
32240             if(this.tabs) this.tabs.bodyEl.clip();
32241             if(this.activePanel){
32242                 this.activePanel.getEl().clip();
32243                 
32244                 if(this.activePanel.beforeSlide){
32245                     this.activePanel.beforeSlide();
32246                 }
32247             }
32248         }
32249     },
32250     
32251     afterSlide : function(){
32252         if(Roo.isGecko){// firefox overflow auto bug workaround
32253             this.bodyEl.unclip();
32254             if(this.tabs) this.tabs.bodyEl.unclip();
32255             if(this.activePanel){
32256                 this.activePanel.getEl().unclip();
32257                 if(this.activePanel.afterSlide){
32258                     this.activePanel.afterSlide();
32259                 }
32260             }
32261         }
32262     },
32263
32264     initAutoHide : function(){
32265         if(this.autoHide !== false){
32266             if(!this.autoHideHd){
32267                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32268                 this.autoHideHd = {
32269                     "mouseout": function(e){
32270                         if(!e.within(this.el, true)){
32271                             st.delay(500);
32272                         }
32273                     },
32274                     "mouseover" : function(e){
32275                         st.cancel();
32276                     },
32277                     scope : this
32278                 };
32279             }
32280             this.el.on(this.autoHideHd);
32281         }
32282     },
32283
32284     clearAutoHide : function(){
32285         if(this.autoHide !== false){
32286             this.el.un("mouseout", this.autoHideHd.mouseout);
32287             this.el.un("mouseover", this.autoHideHd.mouseover);
32288         }
32289     },
32290
32291     clearMonitor : function(){
32292         Roo.get(document).un("click", this.slideInIf, this);
32293     },
32294
32295     // these names are backwards but not changed for compat
32296     slideOut : function(){
32297         if(this.isSlid || this.el.hasActiveFx()){
32298             return;
32299         }
32300         this.isSlid = true;
32301         if(this.collapseBtn){
32302             this.collapseBtn.hide();
32303         }
32304         this.closeBtnState = this.closeBtn.getStyle('display');
32305         this.closeBtn.hide();
32306         if(this.stickBtn){
32307             this.stickBtn.show();
32308         }
32309         this.el.show();
32310         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32311         this.beforeSlide();
32312         this.el.setStyle("z-index", 10001);
32313         this.el.slideIn(this.getSlideAnchor(), {
32314             callback: function(){
32315                 this.afterSlide();
32316                 this.initAutoHide();
32317                 Roo.get(document).on("click", this.slideInIf, this);
32318                 this.fireEvent("slideshow", this);
32319             },
32320             scope: this,
32321             block: true
32322         });
32323     },
32324
32325     afterSlideIn : function(){
32326         this.clearAutoHide();
32327         this.isSlid = false;
32328         this.clearMonitor();
32329         this.el.setStyle("z-index", "");
32330         if(this.collapseBtn){
32331             this.collapseBtn.show();
32332         }
32333         this.closeBtn.setStyle('display', this.closeBtnState);
32334         if(this.stickBtn){
32335             this.stickBtn.hide();
32336         }
32337         this.fireEvent("slidehide", this);
32338     },
32339
32340     slideIn : function(cb){
32341         if(!this.isSlid || this.el.hasActiveFx()){
32342             Roo.callback(cb);
32343             return;
32344         }
32345         this.isSlid = false;
32346         this.beforeSlide();
32347         this.el.slideOut(this.getSlideAnchor(), {
32348             callback: function(){
32349                 this.el.setLeftTop(-10000, -10000);
32350                 this.afterSlide();
32351                 this.afterSlideIn();
32352                 Roo.callback(cb);
32353             },
32354             scope: this,
32355             block: true
32356         });
32357     },
32358     
32359     slideInIf : function(e){
32360         if(!e.within(this.el)){
32361             this.slideIn();
32362         }
32363     },
32364
32365     animateCollapse : function(){
32366         this.beforeSlide();
32367         this.el.setStyle("z-index", 20000);
32368         var anchor = this.getSlideAnchor();
32369         this.el.slideOut(anchor, {
32370             callback : function(){
32371                 this.el.setStyle("z-index", "");
32372                 this.collapsedEl.slideIn(anchor, {duration:.3});
32373                 this.afterSlide();
32374                 this.el.setLocation(-10000,-10000);
32375                 this.el.hide();
32376                 this.fireEvent("collapsed", this);
32377             },
32378             scope: this,
32379             block: true
32380         });
32381     },
32382
32383     animateExpand : function(){
32384         this.beforeSlide();
32385         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32386         this.el.setStyle("z-index", 20000);
32387         this.collapsedEl.hide({
32388             duration:.1
32389         });
32390         this.el.slideIn(this.getSlideAnchor(), {
32391             callback : function(){
32392                 this.el.setStyle("z-index", "");
32393                 this.afterSlide();
32394                 if(this.split){
32395                     this.split.el.show();
32396                 }
32397                 this.fireEvent("invalidated", this);
32398                 this.fireEvent("expanded", this);
32399             },
32400             scope: this,
32401             block: true
32402         });
32403     },
32404
32405     anchors : {
32406         "west" : "left",
32407         "east" : "right",
32408         "north" : "top",
32409         "south" : "bottom"
32410     },
32411
32412     sanchors : {
32413         "west" : "l",
32414         "east" : "r",
32415         "north" : "t",
32416         "south" : "b"
32417     },
32418
32419     canchors : {
32420         "west" : "tl-tr",
32421         "east" : "tr-tl",
32422         "north" : "tl-bl",
32423         "south" : "bl-tl"
32424     },
32425
32426     getAnchor : function(){
32427         return this.anchors[this.position];
32428     },
32429
32430     getCollapseAnchor : function(){
32431         return this.canchors[this.position];
32432     },
32433
32434     getSlideAnchor : function(){
32435         return this.sanchors[this.position];
32436     },
32437
32438     getAlignAdj : function(){
32439         var cm = this.cmargins;
32440         switch(this.position){
32441             case "west":
32442                 return [0, 0];
32443             break;
32444             case "east":
32445                 return [0, 0];
32446             break;
32447             case "north":
32448                 return [0, 0];
32449             break;
32450             case "south":
32451                 return [0, 0];
32452             break;
32453         }
32454     },
32455
32456     getExpandAdj : function(){
32457         var c = this.collapsedEl, cm = this.cmargins;
32458         switch(this.position){
32459             case "west":
32460                 return [-(cm.right+c.getWidth()+cm.left), 0];
32461             break;
32462             case "east":
32463                 return [cm.right+c.getWidth()+cm.left, 0];
32464             break;
32465             case "north":
32466                 return [0, -(cm.top+cm.bottom+c.getHeight())];
32467             break;
32468             case "south":
32469                 return [0, cm.top+cm.bottom+c.getHeight()];
32470             break;
32471         }
32472     }
32473 });/*
32474  * Based on:
32475  * Ext JS Library 1.1.1
32476  * Copyright(c) 2006-2007, Ext JS, LLC.
32477  *
32478  * Originally Released Under LGPL - original licence link has changed is not relivant.
32479  *
32480  * Fork - LGPL
32481  * <script type="text/javascript">
32482  */
32483 /*
32484  * These classes are private internal classes
32485  */
32486 Roo.CenterLayoutRegion = function(mgr, config){
32487     Roo.LayoutRegion.call(this, mgr, config, "center");
32488     this.visible = true;
32489     this.minWidth = config.minWidth || 20;
32490     this.minHeight = config.minHeight || 20;
32491 };
32492
32493 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
32494     hide : function(){
32495         // center panel can't be hidden
32496     },
32497     
32498     show : function(){
32499         // center panel can't be hidden
32500     },
32501     
32502     getMinWidth: function(){
32503         return this.minWidth;
32504     },
32505     
32506     getMinHeight: function(){
32507         return this.minHeight;
32508     }
32509 });
32510
32511
32512 Roo.NorthLayoutRegion = function(mgr, config){
32513     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
32514     if(this.split){
32515         this.split.placement = Roo.SplitBar.TOP;
32516         this.split.orientation = Roo.SplitBar.VERTICAL;
32517         this.split.el.addClass("x-layout-split-v");
32518     }
32519     var size = config.initialSize || config.height;
32520     if(typeof size != "undefined"){
32521         this.el.setHeight(size);
32522     }
32523 };
32524 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
32525     orientation: Roo.SplitBar.VERTICAL,
32526     getBox : function(){
32527         if(this.collapsed){
32528             return this.collapsedEl.getBox();
32529         }
32530         var box = this.el.getBox();
32531         if(this.split){
32532             box.height += this.split.el.getHeight();
32533         }
32534         return box;
32535     },
32536     
32537     updateBox : function(box){
32538         if(this.split && !this.collapsed){
32539             box.height -= this.split.el.getHeight();
32540             this.split.el.setLeft(box.x);
32541             this.split.el.setTop(box.y+box.height);
32542             this.split.el.setWidth(box.width);
32543         }
32544         if(this.collapsed){
32545             this.updateBody(box.width, null);
32546         }
32547         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32548     }
32549 });
32550
32551 Roo.SouthLayoutRegion = function(mgr, config){
32552     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
32553     if(this.split){
32554         this.split.placement = Roo.SplitBar.BOTTOM;
32555         this.split.orientation = Roo.SplitBar.VERTICAL;
32556         this.split.el.addClass("x-layout-split-v");
32557     }
32558     var size = config.initialSize || config.height;
32559     if(typeof size != "undefined"){
32560         this.el.setHeight(size);
32561     }
32562 };
32563 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
32564     orientation: Roo.SplitBar.VERTICAL,
32565     getBox : function(){
32566         if(this.collapsed){
32567             return this.collapsedEl.getBox();
32568         }
32569         var box = this.el.getBox();
32570         if(this.split){
32571             var sh = this.split.el.getHeight();
32572             box.height += sh;
32573             box.y -= sh;
32574         }
32575         return box;
32576     },
32577     
32578     updateBox : function(box){
32579         if(this.split && !this.collapsed){
32580             var sh = this.split.el.getHeight();
32581             box.height -= sh;
32582             box.y += sh;
32583             this.split.el.setLeft(box.x);
32584             this.split.el.setTop(box.y-sh);
32585             this.split.el.setWidth(box.width);
32586         }
32587         if(this.collapsed){
32588             this.updateBody(box.width, null);
32589         }
32590         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32591     }
32592 });
32593
32594 Roo.EastLayoutRegion = function(mgr, config){
32595     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
32596     if(this.split){
32597         this.split.placement = Roo.SplitBar.RIGHT;
32598         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32599         this.split.el.addClass("x-layout-split-h");
32600     }
32601     var size = config.initialSize || config.width;
32602     if(typeof size != "undefined"){
32603         this.el.setWidth(size);
32604     }
32605 };
32606 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
32607     orientation: Roo.SplitBar.HORIZONTAL,
32608     getBox : function(){
32609         if(this.collapsed){
32610             return this.collapsedEl.getBox();
32611         }
32612         var box = this.el.getBox();
32613         if(this.split){
32614             var sw = this.split.el.getWidth();
32615             box.width += sw;
32616             box.x -= sw;
32617         }
32618         return box;
32619     },
32620
32621     updateBox : function(box){
32622         if(this.split && !this.collapsed){
32623             var sw = this.split.el.getWidth();
32624             box.width -= sw;
32625             this.split.el.setLeft(box.x);
32626             this.split.el.setTop(box.y);
32627             this.split.el.setHeight(box.height);
32628             box.x += sw;
32629         }
32630         if(this.collapsed){
32631             this.updateBody(null, box.height);
32632         }
32633         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32634     }
32635 });
32636
32637 Roo.WestLayoutRegion = function(mgr, config){
32638     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
32639     if(this.split){
32640         this.split.placement = Roo.SplitBar.LEFT;
32641         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32642         this.split.el.addClass("x-layout-split-h");
32643     }
32644     var size = config.initialSize || config.width;
32645     if(typeof size != "undefined"){
32646         this.el.setWidth(size);
32647     }
32648 };
32649 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
32650     orientation: Roo.SplitBar.HORIZONTAL,
32651     getBox : function(){
32652         if(this.collapsed){
32653             return this.collapsedEl.getBox();
32654         }
32655         var box = this.el.getBox();
32656         if(this.split){
32657             box.width += this.split.el.getWidth();
32658         }
32659         return box;
32660     },
32661     
32662     updateBox : function(box){
32663         if(this.split && !this.collapsed){
32664             var sw = this.split.el.getWidth();
32665             box.width -= sw;
32666             this.split.el.setLeft(box.x+box.width);
32667             this.split.el.setTop(box.y);
32668             this.split.el.setHeight(box.height);
32669         }
32670         if(this.collapsed){
32671             this.updateBody(null, box.height);
32672         }
32673         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32674     }
32675 });
32676 /*
32677  * Based on:
32678  * Ext JS Library 1.1.1
32679  * Copyright(c) 2006-2007, Ext JS, LLC.
32680  *
32681  * Originally Released Under LGPL - original licence link has changed is not relivant.
32682  *
32683  * Fork - LGPL
32684  * <script type="text/javascript">
32685  */
32686  
32687  
32688 /*
32689  * Private internal class for reading and applying state
32690  */
32691 Roo.LayoutStateManager = function(layout){
32692      // default empty state
32693      this.state = {
32694         north: {},
32695         south: {},
32696         east: {},
32697         west: {}       
32698     };
32699 };
32700
32701 Roo.LayoutStateManager.prototype = {
32702     init : function(layout, provider){
32703         this.provider = provider;
32704         var state = provider.get(layout.id+"-layout-state");
32705         if(state){
32706             var wasUpdating = layout.isUpdating();
32707             if(!wasUpdating){
32708                 layout.beginUpdate();
32709             }
32710             for(var key in state){
32711                 if(typeof state[key] != "function"){
32712                     var rstate = state[key];
32713                     var r = layout.getRegion(key);
32714                     if(r && rstate){
32715                         if(rstate.size){
32716                             r.resizeTo(rstate.size);
32717                         }
32718                         if(rstate.collapsed == true){
32719                             r.collapse(true);
32720                         }else{
32721                             r.expand(null, true);
32722                         }
32723                     }
32724                 }
32725             }
32726             if(!wasUpdating){
32727                 layout.endUpdate();
32728             }
32729             this.state = state; 
32730         }
32731         this.layout = layout;
32732         layout.on("regionresized", this.onRegionResized, this);
32733         layout.on("regioncollapsed", this.onRegionCollapsed, this);
32734         layout.on("regionexpanded", this.onRegionExpanded, this);
32735     },
32736     
32737     storeState : function(){
32738         this.provider.set(this.layout.id+"-layout-state", this.state);
32739     },
32740     
32741     onRegionResized : function(region, newSize){
32742         this.state[region.getPosition()].size = newSize;
32743         this.storeState();
32744     },
32745     
32746     onRegionCollapsed : function(region){
32747         this.state[region.getPosition()].collapsed = true;
32748         this.storeState();
32749     },
32750     
32751     onRegionExpanded : function(region){
32752         this.state[region.getPosition()].collapsed = false;
32753         this.storeState();
32754     }
32755 };/*
32756  * Based on:
32757  * Ext JS Library 1.1.1
32758  * Copyright(c) 2006-2007, Ext JS, LLC.
32759  *
32760  * Originally Released Under LGPL - original licence link has changed is not relivant.
32761  *
32762  * Fork - LGPL
32763  * <script type="text/javascript">
32764  */
32765 /**
32766  * @class Roo.ContentPanel
32767  * @extends Roo.util.Observable
32768  * A basic ContentPanel element.
32769  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
32770  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
32771  * @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
32772  * @cfg {Boolean}   closable      True if the panel can be closed/removed
32773  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
32774  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
32775  * @cfg {Toolbar}   toolbar       A toolbar for this panel
32776  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
32777  * @cfg {String} title          The title for this panel
32778  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
32779  * @cfg {String} url            Calls {@link #setUrl} with this value
32780  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
32781  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
32782  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
32783  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
32784
32785  * @constructor
32786  * Create a new ContentPanel.
32787  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
32788  * @param {String/Object} config A string to set only the title or a config object
32789  * @param {String} content (optional) Set the HTML content for this panel
32790  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
32791  */
32792 Roo.ContentPanel = function(el, config, content){
32793     
32794      
32795     /*
32796     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
32797         config = el;
32798         el = Roo.id();
32799     }
32800     if (config && config.parentLayout) { 
32801         el = config.parentLayout.el.createChild(); 
32802     }
32803     */
32804     if(el.autoCreate){ // xtype is available if this is called from factory
32805         config = el;
32806         el = Roo.id();
32807     }
32808     this.el = Roo.get(el);
32809     if(!this.el && config && config.autoCreate){
32810         if(typeof config.autoCreate == "object"){
32811             if(!config.autoCreate.id){
32812                 config.autoCreate.id = config.id||el;
32813             }
32814             this.el = Roo.DomHelper.append(document.body,
32815                         config.autoCreate, true);
32816         }else{
32817             this.el = Roo.DomHelper.append(document.body,
32818                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
32819         }
32820     }
32821     this.closable = false;
32822     this.loaded = false;
32823     this.active = false;
32824     if(typeof config == "string"){
32825         this.title = config;
32826     }else{
32827         Roo.apply(this, config);
32828     }
32829     
32830     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
32831         this.wrapEl = this.el.wrap();
32832         this.toolbar.container = this.el.insertSibling(false, 'before');
32833         this.toolbar = new Roo.Toolbar(this.toolbar);
32834     }
32835     
32836     
32837     
32838     if(this.resizeEl){
32839         this.resizeEl = Roo.get(this.resizeEl, true);
32840     }else{
32841         this.resizeEl = this.el;
32842     }
32843     this.addEvents({
32844         /**
32845          * @event activate
32846          * Fires when this panel is activated. 
32847          * @param {Roo.ContentPanel} this
32848          */
32849         "activate" : true,
32850         /**
32851          * @event deactivate
32852          * Fires when this panel is activated. 
32853          * @param {Roo.ContentPanel} this
32854          */
32855         "deactivate" : true,
32856
32857         /**
32858          * @event resize
32859          * Fires when this panel is resized if fitToFrame is true.
32860          * @param {Roo.ContentPanel} this
32861          * @param {Number} width The width after any component adjustments
32862          * @param {Number} height The height after any component adjustments
32863          */
32864         "resize" : true,
32865         
32866          /**
32867          * @event render
32868          * Fires when this tab is created
32869          * @param {Roo.ContentPanel} this
32870          */
32871         "render" : true
32872         
32873         
32874         
32875     });
32876     if(this.autoScroll){
32877         this.resizeEl.setStyle("overflow", "auto");
32878     } else {
32879         // fix randome scrolling
32880         this.el.on('scroll', function() {
32881             Roo.log('fix random scolling');
32882             this.scrollTo('top',0); 
32883         });
32884     }
32885     content = content || this.content;
32886     if(content){
32887         this.setContent(content);
32888     }
32889     if(config && config.url){
32890         this.setUrl(this.url, this.params, this.loadOnce);
32891     }
32892     
32893     
32894     
32895     Roo.ContentPanel.superclass.constructor.call(this);
32896     
32897     this.fireEvent('render', this);
32898 };
32899
32900 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
32901     tabTip:'',
32902     setRegion : function(region){
32903         this.region = region;
32904         if(region){
32905            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
32906         }else{
32907            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
32908         } 
32909     },
32910     
32911     /**
32912      * Returns the toolbar for this Panel if one was configured. 
32913      * @return {Roo.Toolbar} 
32914      */
32915     getToolbar : function(){
32916         return this.toolbar;
32917     },
32918     
32919     setActiveState : function(active){
32920         this.active = active;
32921         if(!active){
32922             this.fireEvent("deactivate", this);
32923         }else{
32924             this.fireEvent("activate", this);
32925         }
32926     },
32927     /**
32928      * Updates this panel's element
32929      * @param {String} content The new content
32930      * @param {Boolean} loadScripts (optional) true to look for and process scripts
32931     */
32932     setContent : function(content, loadScripts){
32933         this.el.update(content, loadScripts);
32934     },
32935
32936     ignoreResize : function(w, h){
32937         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
32938             return true;
32939         }else{
32940             this.lastSize = {width: w, height: h};
32941             return false;
32942         }
32943     },
32944     /**
32945      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
32946      * @return {Roo.UpdateManager} The UpdateManager
32947      */
32948     getUpdateManager : function(){
32949         return this.el.getUpdateManager();
32950     },
32951      /**
32952      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
32953      * @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:
32954 <pre><code>
32955 panel.load({
32956     url: "your-url.php",
32957     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
32958     callback: yourFunction,
32959     scope: yourObject, //(optional scope)
32960     discardUrl: false,
32961     nocache: false,
32962     text: "Loading...",
32963     timeout: 30,
32964     scripts: false
32965 });
32966 </code></pre>
32967      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
32968      * 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.
32969      * @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}
32970      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
32971      * @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.
32972      * @return {Roo.ContentPanel} this
32973      */
32974     load : function(){
32975         var um = this.el.getUpdateManager();
32976         um.update.apply(um, arguments);
32977         return this;
32978     },
32979
32980
32981     /**
32982      * 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.
32983      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
32984      * @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)
32985      * @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)
32986      * @return {Roo.UpdateManager} The UpdateManager
32987      */
32988     setUrl : function(url, params, loadOnce){
32989         if(this.refreshDelegate){
32990             this.removeListener("activate", this.refreshDelegate);
32991         }
32992         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
32993         this.on("activate", this.refreshDelegate);
32994         return this.el.getUpdateManager();
32995     },
32996     
32997     _handleRefresh : function(url, params, loadOnce){
32998         if(!loadOnce || !this.loaded){
32999             var updater = this.el.getUpdateManager();
33000             updater.update(url, params, this._setLoaded.createDelegate(this));
33001         }
33002     },
33003     
33004     _setLoaded : function(){
33005         this.loaded = true;
33006     }, 
33007     
33008     /**
33009      * Returns this panel's id
33010      * @return {String} 
33011      */
33012     getId : function(){
33013         return this.el.id;
33014     },
33015     
33016     /** 
33017      * Returns this panel's element - used by regiosn to add.
33018      * @return {Roo.Element} 
33019      */
33020     getEl : function(){
33021         return this.wrapEl || this.el;
33022     },
33023     
33024     adjustForComponents : function(width, height){
33025         if(this.resizeEl != this.el){
33026             width -= this.el.getFrameWidth('lr');
33027             height -= this.el.getFrameWidth('tb');
33028         }
33029         if(this.toolbar){
33030             var te = this.toolbar.getEl();
33031             height -= te.getHeight();
33032             te.setWidth(width);
33033         }
33034         if(this.adjustments){
33035             width += this.adjustments[0];
33036             height += this.adjustments[1];
33037         }
33038         return {"width": width, "height": height};
33039     },
33040     
33041     setSize : function(width, height){
33042         if(this.fitToFrame && !this.ignoreResize(width, height)){
33043             if(this.fitContainer && this.resizeEl != this.el){
33044                 this.el.setSize(width, height);
33045             }
33046             var size = this.adjustForComponents(width, height);
33047             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33048             this.fireEvent('resize', this, size.width, size.height);
33049         }
33050     },
33051     
33052     /**
33053      * Returns this panel's title
33054      * @return {String} 
33055      */
33056     getTitle : function(){
33057         return this.title;
33058     },
33059     
33060     /**
33061      * Set this panel's title
33062      * @param {String} title
33063      */
33064     setTitle : function(title){
33065         this.title = title;
33066         if(this.region){
33067             this.region.updatePanelTitle(this, title);
33068         }
33069     },
33070     
33071     /**
33072      * Returns true is this panel was configured to be closable
33073      * @return {Boolean} 
33074      */
33075     isClosable : function(){
33076         return this.closable;
33077     },
33078     
33079     beforeSlide : function(){
33080         this.el.clip();
33081         this.resizeEl.clip();
33082     },
33083     
33084     afterSlide : function(){
33085         this.el.unclip();
33086         this.resizeEl.unclip();
33087     },
33088     
33089     /**
33090      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33091      *   Will fail silently if the {@link #setUrl} method has not been called.
33092      *   This does not activate the panel, just updates its content.
33093      */
33094     refresh : function(){
33095         if(this.refreshDelegate){
33096            this.loaded = false;
33097            this.refreshDelegate();
33098         }
33099     },
33100     
33101     /**
33102      * Destroys this panel
33103      */
33104     destroy : function(){
33105         this.el.removeAllListeners();
33106         var tempEl = document.createElement("span");
33107         tempEl.appendChild(this.el.dom);
33108         tempEl.innerHTML = "";
33109         this.el.remove();
33110         this.el = null;
33111     },
33112     
33113     /**
33114      * form - if the content panel contains a form - this is a reference to it.
33115      * @type {Roo.form.Form}
33116      */
33117     form : false,
33118     /**
33119      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33120      *    This contains a reference to it.
33121      * @type {Roo.View}
33122      */
33123     view : false,
33124     
33125       /**
33126      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33127      * <pre><code>
33128
33129 layout.addxtype({
33130        xtype : 'Form',
33131        items: [ .... ]
33132    }
33133 );
33134
33135 </code></pre>
33136      * @param {Object} cfg Xtype definition of item to add.
33137      */
33138     
33139     addxtype : function(cfg) {
33140         // add form..
33141         if (cfg.xtype.match(/^Form$/)) {
33142             var el = this.el.createChild();
33143
33144             this.form = new  Roo.form.Form(cfg);
33145             
33146             
33147             if ( this.form.allItems.length) this.form.render(el.dom);
33148             return this.form;
33149         }
33150         // should only have one of theses..
33151         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33152             // views..
33153             cfg.el = this.el.appendChild(document.createElement("div"));
33154             // factory?
33155             
33156             var ret = new Roo.factory(cfg);
33157             ret.render && ret.render(false, ''); // render blank..
33158             this.view = ret;
33159             return ret;
33160         }
33161         return false;
33162     }
33163 });
33164
33165 /**
33166  * @class Roo.GridPanel
33167  * @extends Roo.ContentPanel
33168  * @constructor
33169  * Create a new GridPanel.
33170  * @param {Roo.grid.Grid} grid The grid for this panel
33171  * @param {String/Object} config A string to set only the panel's title, or a config object
33172  */
33173 Roo.GridPanel = function(grid, config){
33174     
33175   
33176     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33177         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33178         
33179     this.wrapper.dom.appendChild(grid.getGridEl().dom);
33180     
33181     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
33182     
33183     if(this.toolbar){
33184         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33185     }
33186     // xtype created footer. - not sure if will work as we normally have to render first..
33187     if (this.footer && !this.footer.el && this.footer.xtype) {
33188         
33189         this.footer.container = this.grid.getView().getFooterPanel(true);
33190         this.footer.dataSource = this.grid.dataSource;
33191         this.footer = Roo.factory(this.footer, Roo);
33192         
33193     }
33194     
33195     grid.monitorWindowResize = false; // turn off autosizing
33196     grid.autoHeight = false;
33197     grid.autoWidth = false;
33198     this.grid = grid;
33199     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33200 };
33201
33202 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
33203     getId : function(){
33204         return this.grid.id;
33205     },
33206     
33207     /**
33208      * Returns the grid for this panel
33209      * @return {Roo.grid.Grid} 
33210      */
33211     getGrid : function(){
33212         return this.grid;    
33213     },
33214     
33215     setSize : function(width, height){
33216         if(!this.ignoreResize(width, height)){
33217             var grid = this.grid;
33218             var size = this.adjustForComponents(width, height);
33219             grid.getGridEl().setSize(size.width, size.height);
33220             grid.autoSize();
33221         }
33222     },
33223     
33224     beforeSlide : function(){
33225         this.grid.getView().scroller.clip();
33226     },
33227     
33228     afterSlide : function(){
33229         this.grid.getView().scroller.unclip();
33230     },
33231     
33232     destroy : function(){
33233         this.grid.destroy();
33234         delete this.grid;
33235         Roo.GridPanel.superclass.destroy.call(this); 
33236     }
33237 });
33238
33239
33240 /**
33241  * @class Roo.NestedLayoutPanel
33242  * @extends Roo.ContentPanel
33243  * @constructor
33244  * Create a new NestedLayoutPanel.
33245  * 
33246  * 
33247  * @param {Roo.BorderLayout} layout The layout for this panel
33248  * @param {String/Object} config A string to set only the title or a config object
33249  */
33250 Roo.NestedLayoutPanel = function(layout, config)
33251 {
33252     // construct with only one argument..
33253     /* FIXME - implement nicer consturctors
33254     if (layout.layout) {
33255         config = layout;
33256         layout = config.layout;
33257         delete config.layout;
33258     }
33259     if (layout.xtype && !layout.getEl) {
33260         // then layout needs constructing..
33261         layout = Roo.factory(layout, Roo);
33262     }
33263     */
33264     
33265     
33266     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
33267     
33268     layout.monitorWindowResize = false; // turn off autosizing
33269     this.layout = layout;
33270     this.layout.getEl().addClass("x-layout-nested-layout");
33271     
33272     
33273     
33274     
33275 };
33276
33277 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
33278
33279     setSize : function(width, height){
33280         if(!this.ignoreResize(width, height)){
33281             var size = this.adjustForComponents(width, height);
33282             var el = this.layout.getEl();
33283             el.setSize(size.width, size.height);
33284             var touch = el.dom.offsetWidth;
33285             this.layout.layout();
33286             // ie requires a double layout on the first pass
33287             if(Roo.isIE && !this.initialized){
33288                 this.initialized = true;
33289                 this.layout.layout();
33290             }
33291         }
33292     },
33293     
33294     // activate all subpanels if not currently active..
33295     
33296     setActiveState : function(active){
33297         this.active = active;
33298         if(!active){
33299             this.fireEvent("deactivate", this);
33300             return;
33301         }
33302         
33303         this.fireEvent("activate", this);
33304         // not sure if this should happen before or after..
33305         if (!this.layout) {
33306             return; // should not happen..
33307         }
33308         var reg = false;
33309         for (var r in this.layout.regions) {
33310             reg = this.layout.getRegion(r);
33311             if (reg.getActivePanel()) {
33312                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
33313                 reg.setActivePanel(reg.getActivePanel());
33314                 continue;
33315             }
33316             if (!reg.panels.length) {
33317                 continue;
33318             }
33319             reg.showPanel(reg.getPanel(0));
33320         }
33321         
33322         
33323         
33324         
33325     },
33326     
33327     /**
33328      * Returns the nested BorderLayout for this panel
33329      * @return {Roo.BorderLayout} 
33330      */
33331     getLayout : function(){
33332         return this.layout;
33333     },
33334     
33335      /**
33336      * Adds a xtype elements to the layout of the nested panel
33337      * <pre><code>
33338
33339 panel.addxtype({
33340        xtype : 'ContentPanel',
33341        region: 'west',
33342        items: [ .... ]
33343    }
33344 );
33345
33346 panel.addxtype({
33347         xtype : 'NestedLayoutPanel',
33348         region: 'west',
33349         layout: {
33350            center: { },
33351            west: { }   
33352         },
33353         items : [ ... list of content panels or nested layout panels.. ]
33354    }
33355 );
33356 </code></pre>
33357      * @param {Object} cfg Xtype definition of item to add.
33358      */
33359     addxtype : function(cfg) {
33360         return this.layout.addxtype(cfg);
33361     
33362     }
33363 });
33364
33365 Roo.ScrollPanel = function(el, config, content){
33366     config = config || {};
33367     config.fitToFrame = true;
33368     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
33369     
33370     this.el.dom.style.overflow = "hidden";
33371     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
33372     this.el.removeClass("x-layout-inactive-content");
33373     this.el.on("mousewheel", this.onWheel, this);
33374
33375     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
33376     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
33377     up.unselectable(); down.unselectable();
33378     up.on("click", this.scrollUp, this);
33379     down.on("click", this.scrollDown, this);
33380     up.addClassOnOver("x-scroller-btn-over");
33381     down.addClassOnOver("x-scroller-btn-over");
33382     up.addClassOnClick("x-scroller-btn-click");
33383     down.addClassOnClick("x-scroller-btn-click");
33384     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
33385
33386     this.resizeEl = this.el;
33387     this.el = wrap; this.up = up; this.down = down;
33388 };
33389
33390 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
33391     increment : 100,
33392     wheelIncrement : 5,
33393     scrollUp : function(){
33394         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
33395     },
33396
33397     scrollDown : function(){
33398         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
33399     },
33400
33401     afterScroll : function(){
33402         var el = this.resizeEl;
33403         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
33404         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33405         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33406     },
33407
33408     setSize : function(){
33409         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
33410         this.afterScroll();
33411     },
33412
33413     onWheel : function(e){
33414         var d = e.getWheelDelta();
33415         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
33416         this.afterScroll();
33417         e.stopEvent();
33418     },
33419
33420     setContent : function(content, loadScripts){
33421         this.resizeEl.update(content, loadScripts);
33422     }
33423
33424 });
33425
33426
33427
33428
33429
33430
33431
33432
33433
33434 /**
33435  * @class Roo.TreePanel
33436  * @extends Roo.ContentPanel
33437  * @constructor
33438  * Create a new TreePanel. - defaults to fit/scoll contents.
33439  * @param {String/Object} config A string to set only the panel's title, or a config object
33440  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
33441  */
33442 Roo.TreePanel = function(config){
33443     var el = config.el;
33444     var tree = config.tree;
33445     delete config.tree; 
33446     delete config.el; // hopefull!
33447     
33448     // wrapper for IE7 strict & safari scroll issue
33449     
33450     var treeEl = el.createChild();
33451     config.resizeEl = treeEl;
33452     
33453     
33454     
33455     Roo.TreePanel.superclass.constructor.call(this, el, config);
33456  
33457  
33458     this.tree = new Roo.tree.TreePanel(treeEl , tree);
33459     //console.log(tree);
33460     this.on('activate', function()
33461     {
33462         if (this.tree.rendered) {
33463             return;
33464         }
33465         //console.log('render tree');
33466         this.tree.render();
33467     });
33468     
33469     this.on('resize',  function (cp, w, h) {
33470             this.tree.innerCt.setWidth(w);
33471             this.tree.innerCt.setHeight(h);
33472             this.tree.innerCt.setStyle('overflow-y', 'auto');
33473     });
33474
33475         
33476     
33477 };
33478
33479 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
33480     fitToFrame : true,
33481     autoScroll : true
33482 });
33483
33484
33485
33486
33487
33488
33489
33490
33491
33492
33493
33494 /*
33495  * Based on:
33496  * Ext JS Library 1.1.1
33497  * Copyright(c) 2006-2007, Ext JS, LLC.
33498  *
33499  * Originally Released Under LGPL - original licence link has changed is not relivant.
33500  *
33501  * Fork - LGPL
33502  * <script type="text/javascript">
33503  */
33504  
33505
33506 /**
33507  * @class Roo.ReaderLayout
33508  * @extends Roo.BorderLayout
33509  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
33510  * center region containing two nested regions (a top one for a list view and one for item preview below),
33511  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
33512  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
33513  * expedites the setup of the overall layout and regions for this common application style.
33514  * Example:
33515  <pre><code>
33516 var reader = new Roo.ReaderLayout();
33517 var CP = Roo.ContentPanel;  // shortcut for adding
33518
33519 reader.beginUpdate();
33520 reader.add("north", new CP("north", "North"));
33521 reader.add("west", new CP("west", {title: "West"}));
33522 reader.add("east", new CP("east", {title: "East"}));
33523
33524 reader.regions.listView.add(new CP("listView", "List"));
33525 reader.regions.preview.add(new CP("preview", "Preview"));
33526 reader.endUpdate();
33527 </code></pre>
33528 * @constructor
33529 * Create a new ReaderLayout
33530 * @param {Object} config Configuration options
33531 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
33532 * document.body if omitted)
33533 */
33534 Roo.ReaderLayout = function(config, renderTo){
33535     var c = config || {size:{}};
33536     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
33537         north: c.north !== false ? Roo.apply({
33538             split:false,
33539             initialSize: 32,
33540             titlebar: false
33541         }, c.north) : false,
33542         west: c.west !== false ? Roo.apply({
33543             split:true,
33544             initialSize: 200,
33545             minSize: 175,
33546             maxSize: 400,
33547             titlebar: true,
33548             collapsible: true,
33549             animate: true,
33550             margins:{left:5,right:0,bottom:5,top:5},
33551             cmargins:{left:5,right:5,bottom:5,top:5}
33552         }, c.west) : false,
33553         east: c.east !== false ? Roo.apply({
33554             split:true,
33555             initialSize: 200,
33556             minSize: 175,
33557             maxSize: 400,
33558             titlebar: true,
33559             collapsible: true,
33560             animate: true,
33561             margins:{left:0,right:5,bottom:5,top:5},
33562             cmargins:{left:5,right:5,bottom:5,top:5}
33563         }, c.east) : false,
33564         center: Roo.apply({
33565             tabPosition: 'top',
33566             autoScroll:false,
33567             closeOnTab: true,
33568             titlebar:false,
33569             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
33570         }, c.center)
33571     });
33572
33573     this.el.addClass('x-reader');
33574
33575     this.beginUpdate();
33576
33577     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
33578         south: c.preview !== false ? Roo.apply({
33579             split:true,
33580             initialSize: 200,
33581             minSize: 100,
33582             autoScroll:true,
33583             collapsible:true,
33584             titlebar: true,
33585             cmargins:{top:5,left:0, right:0, bottom:0}
33586         }, c.preview) : false,
33587         center: Roo.apply({
33588             autoScroll:false,
33589             titlebar:false,
33590             minHeight:200
33591         }, c.listView)
33592     });
33593     this.add('center', new Roo.NestedLayoutPanel(inner,
33594             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
33595
33596     this.endUpdate();
33597
33598     this.regions.preview = inner.getRegion('south');
33599     this.regions.listView = inner.getRegion('center');
33600 };
33601
33602 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
33603  * Based on:
33604  * Ext JS Library 1.1.1
33605  * Copyright(c) 2006-2007, Ext JS, LLC.
33606  *
33607  * Originally Released Under LGPL - original licence link has changed is not relivant.
33608  *
33609  * Fork - LGPL
33610  * <script type="text/javascript">
33611  */
33612  
33613 /**
33614  * @class Roo.grid.Grid
33615  * @extends Roo.util.Observable
33616  * This class represents the primary interface of a component based grid control.
33617  * <br><br>Usage:<pre><code>
33618  var grid = new Roo.grid.Grid("my-container-id", {
33619      ds: myDataStore,
33620      cm: myColModel,
33621      selModel: mySelectionModel,
33622      autoSizeColumns: true,
33623      monitorWindowResize: false,
33624      trackMouseOver: true
33625  });
33626  // set any options
33627  grid.render();
33628  * </code></pre>
33629  * <b>Common Problems:</b><br/>
33630  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
33631  * element will correct this<br/>
33632  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
33633  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
33634  * are unpredictable.<br/>
33635  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
33636  * grid to calculate dimensions/offsets.<br/>
33637   * @constructor
33638  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
33639  * The container MUST have some type of size defined for the grid to fill. The container will be
33640  * automatically set to position relative if it isn't already.
33641  * @param {Object} config A config object that sets properties on this grid.
33642  */
33643 Roo.grid.Grid = function(container, config){
33644         // initialize the container
33645         this.container = Roo.get(container);
33646         this.container.update("");
33647         this.container.setStyle("overflow", "hidden");
33648     this.container.addClass('x-grid-container');
33649
33650     this.id = this.container.id;
33651
33652     Roo.apply(this, config);
33653     // check and correct shorthanded configs
33654     if(this.ds){
33655         this.dataSource = this.ds;
33656         delete this.ds;
33657     }
33658     if(this.cm){
33659         this.colModel = this.cm;
33660         delete this.cm;
33661     }
33662     if(this.sm){
33663         this.selModel = this.sm;
33664         delete this.sm;
33665     }
33666
33667     if (this.selModel) {
33668         this.selModel = Roo.factory(this.selModel, Roo.grid);
33669         this.sm = this.selModel;
33670         this.sm.xmodule = this.xmodule || false;
33671     }
33672     if (typeof(this.colModel.config) == 'undefined') {
33673         this.colModel = new Roo.grid.ColumnModel(this.colModel);
33674         this.cm = this.colModel;
33675         this.cm.xmodule = this.xmodule || false;
33676     }
33677     if (this.dataSource) {
33678         this.dataSource= Roo.factory(this.dataSource, Roo.data);
33679         this.ds = this.dataSource;
33680         this.ds.xmodule = this.xmodule || false;
33681          
33682     }
33683     
33684     
33685     
33686     if(this.width){
33687         this.container.setWidth(this.width);
33688     }
33689
33690     if(this.height){
33691         this.container.setHeight(this.height);
33692     }
33693     /** @private */
33694         this.addEvents({
33695         // raw events
33696         /**
33697          * @event click
33698          * The raw click event for the entire grid.
33699          * @param {Roo.EventObject} e
33700          */
33701         "click" : true,
33702         /**
33703          * @event dblclick
33704          * The raw dblclick event for the entire grid.
33705          * @param {Roo.EventObject} e
33706          */
33707         "dblclick" : true,
33708         /**
33709          * @event contextmenu
33710          * The raw contextmenu event for the entire grid.
33711          * @param {Roo.EventObject} e
33712          */
33713         "contextmenu" : true,
33714         /**
33715          * @event mousedown
33716          * The raw mousedown event for the entire grid.
33717          * @param {Roo.EventObject} e
33718          */
33719         "mousedown" : true,
33720         /**
33721          * @event mouseup
33722          * The raw mouseup event for the entire grid.
33723          * @param {Roo.EventObject} e
33724          */
33725         "mouseup" : true,
33726         /**
33727          * @event mouseover
33728          * The raw mouseover event for the entire grid.
33729          * @param {Roo.EventObject} e
33730          */
33731         "mouseover" : true,
33732         /**
33733          * @event mouseout
33734          * The raw mouseout event for the entire grid.
33735          * @param {Roo.EventObject} e
33736          */
33737         "mouseout" : true,
33738         /**
33739          * @event keypress
33740          * The raw keypress event for the entire grid.
33741          * @param {Roo.EventObject} e
33742          */
33743         "keypress" : true,
33744         /**
33745          * @event keydown
33746          * The raw keydown event for the entire grid.
33747          * @param {Roo.EventObject} e
33748          */
33749         "keydown" : true,
33750
33751         // custom events
33752
33753         /**
33754          * @event cellclick
33755          * Fires when a cell is clicked
33756          * @param {Grid} this
33757          * @param {Number} rowIndex
33758          * @param {Number} columnIndex
33759          * @param {Roo.EventObject} e
33760          */
33761         "cellclick" : true,
33762         /**
33763          * @event celldblclick
33764          * Fires when a cell is double clicked
33765          * @param {Grid} this
33766          * @param {Number} rowIndex
33767          * @param {Number} columnIndex
33768          * @param {Roo.EventObject} e
33769          */
33770         "celldblclick" : true,
33771         /**
33772          * @event rowclick
33773          * Fires when a row is clicked
33774          * @param {Grid} this
33775          * @param {Number} rowIndex
33776          * @param {Roo.EventObject} e
33777          */
33778         "rowclick" : true,
33779         /**
33780          * @event rowdblclick
33781          * Fires when a row is double clicked
33782          * @param {Grid} this
33783          * @param {Number} rowIndex
33784          * @param {Roo.EventObject} e
33785          */
33786         "rowdblclick" : true,
33787         /**
33788          * @event headerclick
33789          * Fires when a header is clicked
33790          * @param {Grid} this
33791          * @param {Number} columnIndex
33792          * @param {Roo.EventObject} e
33793          */
33794         "headerclick" : true,
33795         /**
33796          * @event headerdblclick
33797          * Fires when a header cell is double clicked
33798          * @param {Grid} this
33799          * @param {Number} columnIndex
33800          * @param {Roo.EventObject} e
33801          */
33802         "headerdblclick" : true,
33803         /**
33804          * @event rowcontextmenu
33805          * Fires when a row is right clicked
33806          * @param {Grid} this
33807          * @param {Number} rowIndex
33808          * @param {Roo.EventObject} e
33809          */
33810         "rowcontextmenu" : true,
33811         /**
33812          * @event cellcontextmenu
33813          * Fires when a cell is right clicked
33814          * @param {Grid} this
33815          * @param {Number} rowIndex
33816          * @param {Number} cellIndex
33817          * @param {Roo.EventObject} e
33818          */
33819          "cellcontextmenu" : true,
33820         /**
33821          * @event headercontextmenu
33822          * Fires when a header is right clicked
33823          * @param {Grid} this
33824          * @param {Number} columnIndex
33825          * @param {Roo.EventObject} e
33826          */
33827         "headercontextmenu" : true,
33828         /**
33829          * @event bodyscroll
33830          * Fires when the body element is scrolled
33831          * @param {Number} scrollLeft
33832          * @param {Number} scrollTop
33833          */
33834         "bodyscroll" : true,
33835         /**
33836          * @event columnresize
33837          * Fires when the user resizes a column
33838          * @param {Number} columnIndex
33839          * @param {Number} newSize
33840          */
33841         "columnresize" : true,
33842         /**
33843          * @event columnmove
33844          * Fires when the user moves a column
33845          * @param {Number} oldIndex
33846          * @param {Number} newIndex
33847          */
33848         "columnmove" : true,
33849         /**
33850          * @event startdrag
33851          * Fires when row(s) start being dragged
33852          * @param {Grid} this
33853          * @param {Roo.GridDD} dd The drag drop object
33854          * @param {event} e The raw browser event
33855          */
33856         "startdrag" : true,
33857         /**
33858          * @event enddrag
33859          * Fires when a drag operation is complete
33860          * @param {Grid} this
33861          * @param {Roo.GridDD} dd The drag drop object
33862          * @param {event} e The raw browser event
33863          */
33864         "enddrag" : true,
33865         /**
33866          * @event dragdrop
33867          * Fires when dragged row(s) are dropped on a valid DD target
33868          * @param {Grid} this
33869          * @param {Roo.GridDD} dd The drag drop object
33870          * @param {String} targetId The target drag drop object
33871          * @param {event} e The raw browser event
33872          */
33873         "dragdrop" : true,
33874         /**
33875          * @event dragover
33876          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
33877          * @param {Grid} this
33878          * @param {Roo.GridDD} dd The drag drop object
33879          * @param {String} targetId The target drag drop object
33880          * @param {event} e The raw browser event
33881          */
33882         "dragover" : true,
33883         /**
33884          * @event dragenter
33885          *  Fires when the dragged row(s) first cross another DD target while being dragged
33886          * @param {Grid} this
33887          * @param {Roo.GridDD} dd The drag drop object
33888          * @param {String} targetId The target drag drop object
33889          * @param {event} e The raw browser event
33890          */
33891         "dragenter" : true,
33892         /**
33893          * @event dragout
33894          * Fires when the dragged row(s) leave another DD target while being dragged
33895          * @param {Grid} this
33896          * @param {Roo.GridDD} dd The drag drop object
33897          * @param {String} targetId The target drag drop object
33898          * @param {event} e The raw browser event
33899          */
33900         "dragout" : true,
33901         /**
33902          * @event rowclass
33903          * Fires when a row is rendered, so you can change add a style to it.
33904          * @param {GridView} gridview   The grid view
33905          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
33906          */
33907         'rowclass' : true,
33908
33909         /**
33910          * @event render
33911          * Fires when the grid is rendered
33912          * @param {Grid} grid
33913          */
33914         'render' : true
33915     });
33916
33917     Roo.grid.Grid.superclass.constructor.call(this);
33918 };
33919 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
33920     
33921     /**
33922      * @cfg {String} ddGroup - drag drop group.
33923      */
33924
33925     /**
33926      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
33927      */
33928     minColumnWidth : 25,
33929
33930     /**
33931      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
33932      * <b>on initial render.</b> It is more efficient to explicitly size the columns
33933      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
33934      */
33935     autoSizeColumns : false,
33936
33937     /**
33938      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
33939      */
33940     autoSizeHeaders : true,
33941
33942     /**
33943      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
33944      */
33945     monitorWindowResize : true,
33946
33947     /**
33948      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
33949      * rows measured to get a columns size. Default is 0 (all rows).
33950      */
33951     maxRowsToMeasure : 0,
33952
33953     /**
33954      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
33955      */
33956     trackMouseOver : true,
33957
33958     /**
33959     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
33960     */
33961     
33962     /**
33963     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
33964     */
33965     enableDragDrop : false,
33966     
33967     /**
33968     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
33969     */
33970     enableColumnMove : true,
33971     
33972     /**
33973     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
33974     */
33975     enableColumnHide : true,
33976     
33977     /**
33978     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
33979     */
33980     enableRowHeightSync : false,
33981     
33982     /**
33983     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
33984     */
33985     stripeRows : true,
33986     
33987     /**
33988     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
33989     */
33990     autoHeight : false,
33991
33992     /**
33993      * @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.
33994      */
33995     autoExpandColumn : false,
33996
33997     /**
33998     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
33999     * Default is 50.
34000     */
34001     autoExpandMin : 50,
34002
34003     /**
34004     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
34005     */
34006     autoExpandMax : 1000,
34007
34008     /**
34009     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
34010     */
34011     view : null,
34012
34013     /**
34014     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
34015     */
34016     loadMask : false,
34017     /**
34018     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
34019     */
34020     dropTarget: false,
34021     
34022    
34023     
34024     // private
34025     rendered : false,
34026
34027     /**
34028     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
34029     * of a fixed width. Default is false.
34030     */
34031     /**
34032     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
34033     */
34034     /**
34035      * Called once after all setup has been completed and the grid is ready to be rendered.
34036      * @return {Roo.grid.Grid} this
34037      */
34038     render : function()
34039     {
34040         var c = this.container;
34041         // try to detect autoHeight/width mode
34042         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
34043             this.autoHeight = true;
34044         }
34045         var view = this.getView();
34046         view.init(this);
34047
34048         c.on("click", this.onClick, this);
34049         c.on("dblclick", this.onDblClick, this);
34050         c.on("contextmenu", this.onContextMenu, this);
34051         c.on("keydown", this.onKeyDown, this);
34052
34053         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
34054
34055         this.getSelectionModel().init(this);
34056
34057         view.render();
34058
34059         if(this.loadMask){
34060             this.loadMask = new Roo.LoadMask(this.container,
34061                     Roo.apply({store:this.dataSource}, this.loadMask));
34062         }
34063         
34064         
34065         if (this.toolbar && this.toolbar.xtype) {
34066             this.toolbar.container = this.getView().getHeaderPanel(true);
34067             this.toolbar = new Roo.Toolbar(this.toolbar);
34068         }
34069         if (this.footer && this.footer.xtype) {
34070             this.footer.dataSource = this.getDataSource();
34071             this.footer.container = this.getView().getFooterPanel(true);
34072             this.footer = Roo.factory(this.footer, Roo);
34073         }
34074         if (this.dropTarget && this.dropTarget.xtype) {
34075             delete this.dropTarget.xtype;
34076             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
34077         }
34078         
34079         
34080         this.rendered = true;
34081         this.fireEvent('render', this);
34082         return this;
34083     },
34084
34085         /**
34086          * Reconfigures the grid to use a different Store and Column Model.
34087          * The View will be bound to the new objects and refreshed.
34088          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
34089          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
34090          */
34091     reconfigure : function(dataSource, colModel){
34092         if(this.loadMask){
34093             this.loadMask.destroy();
34094             this.loadMask = new Roo.LoadMask(this.container,
34095                     Roo.apply({store:dataSource}, this.loadMask));
34096         }
34097         this.view.bind(dataSource, colModel);
34098         this.dataSource = dataSource;
34099         this.colModel = colModel;
34100         this.view.refresh(true);
34101     },
34102
34103     // private
34104     onKeyDown : function(e){
34105         this.fireEvent("keydown", e);
34106     },
34107
34108     /**
34109      * Destroy this grid.
34110      * @param {Boolean} removeEl True to remove the element
34111      */
34112     destroy : function(removeEl, keepListeners){
34113         if(this.loadMask){
34114             this.loadMask.destroy();
34115         }
34116         var c = this.container;
34117         c.removeAllListeners();
34118         this.view.destroy();
34119         this.colModel.purgeListeners();
34120         if(!keepListeners){
34121             this.purgeListeners();
34122         }
34123         c.update("");
34124         if(removeEl === true){
34125             c.remove();
34126         }
34127     },
34128
34129     // private
34130     processEvent : function(name, e){
34131         this.fireEvent(name, e);
34132         var t = e.getTarget();
34133         var v = this.view;
34134         var header = v.findHeaderIndex(t);
34135         if(header !== false){
34136             this.fireEvent("header" + name, this, header, e);
34137         }else{
34138             var row = v.findRowIndex(t);
34139             var cell = v.findCellIndex(t);
34140             if(row !== false){
34141                 this.fireEvent("row" + name, this, row, e);
34142                 if(cell !== false){
34143                     this.fireEvent("cell" + name, this, row, cell, e);
34144                 }
34145             }
34146         }
34147     },
34148
34149     // private
34150     onClick : function(e){
34151         this.processEvent("click", e);
34152     },
34153
34154     // private
34155     onContextMenu : function(e, t){
34156         this.processEvent("contextmenu", e);
34157     },
34158
34159     // private
34160     onDblClick : function(e){
34161         this.processEvent("dblclick", e);
34162     },
34163
34164     // private
34165     walkCells : function(row, col, step, fn, scope){
34166         var cm = this.colModel, clen = cm.getColumnCount();
34167         var ds = this.dataSource, rlen = ds.getCount(), first = true;
34168         if(step < 0){
34169             if(col < 0){
34170                 row--;
34171                 first = false;
34172             }
34173             while(row >= 0){
34174                 if(!first){
34175                     col = clen-1;
34176                 }
34177                 first = false;
34178                 while(col >= 0){
34179                     if(fn.call(scope || this, row, col, cm) === true){
34180                         return [row, col];
34181                     }
34182                     col--;
34183                 }
34184                 row--;
34185             }
34186         } else {
34187             if(col >= clen){
34188                 row++;
34189                 first = false;
34190             }
34191             while(row < rlen){
34192                 if(!first){
34193                     col = 0;
34194                 }
34195                 first = false;
34196                 while(col < clen){
34197                     if(fn.call(scope || this, row, col, cm) === true){
34198                         return [row, col];
34199                     }
34200                     col++;
34201                 }
34202                 row++;
34203             }
34204         }
34205         return null;
34206     },
34207
34208     // private
34209     getSelections : function(){
34210         return this.selModel.getSelections();
34211     },
34212
34213     /**
34214      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
34215      * but if manual update is required this method will initiate it.
34216      */
34217     autoSize : function(){
34218         if(this.rendered){
34219             this.view.layout();
34220             if(this.view.adjustForScroll){
34221                 this.view.adjustForScroll();
34222             }
34223         }
34224     },
34225
34226     /**
34227      * Returns the grid's underlying element.
34228      * @return {Element} The element
34229      */
34230     getGridEl : function(){
34231         return this.container;
34232     },
34233
34234     // private for compatibility, overridden by editor grid
34235     stopEditing : function(){},
34236
34237     /**
34238      * Returns the grid's SelectionModel.
34239      * @return {SelectionModel}
34240      */
34241     getSelectionModel : function(){
34242         if(!this.selModel){
34243             this.selModel = new Roo.grid.RowSelectionModel();
34244         }
34245         return this.selModel;
34246     },
34247
34248     /**
34249      * Returns the grid's DataSource.
34250      * @return {DataSource}
34251      */
34252     getDataSource : function(){
34253         return this.dataSource;
34254     },
34255
34256     /**
34257      * Returns the grid's ColumnModel.
34258      * @return {ColumnModel}
34259      */
34260     getColumnModel : function(){
34261         return this.colModel;
34262     },
34263
34264     /**
34265      * Returns the grid's GridView object.
34266      * @return {GridView}
34267      */
34268     getView : function(){
34269         if(!this.view){
34270             this.view = new Roo.grid.GridView(this.viewConfig);
34271         }
34272         return this.view;
34273     },
34274     /**
34275      * Called to get grid's drag proxy text, by default returns this.ddText.
34276      * @return {String}
34277      */
34278     getDragDropText : function(){
34279         var count = this.selModel.getCount();
34280         return String.format(this.ddText, count, count == 1 ? '' : 's');
34281     }
34282 });
34283 /**
34284  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
34285  * %0 is replaced with the number of selected rows.
34286  * @type String
34287  */
34288 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
34289  * Based on:
34290  * Ext JS Library 1.1.1
34291  * Copyright(c) 2006-2007, Ext JS, LLC.
34292  *
34293  * Originally Released Under LGPL - original licence link has changed is not relivant.
34294  *
34295  * Fork - LGPL
34296  * <script type="text/javascript">
34297  */
34298  
34299 Roo.grid.AbstractGridView = function(){
34300         this.grid = null;
34301         
34302         this.events = {
34303             "beforerowremoved" : true,
34304             "beforerowsinserted" : true,
34305             "beforerefresh" : true,
34306             "rowremoved" : true,
34307             "rowsinserted" : true,
34308             "rowupdated" : true,
34309             "refresh" : true
34310         };
34311     Roo.grid.AbstractGridView.superclass.constructor.call(this);
34312 };
34313
34314 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
34315     rowClass : "x-grid-row",
34316     cellClass : "x-grid-cell",
34317     tdClass : "x-grid-td",
34318     hdClass : "x-grid-hd",
34319     splitClass : "x-grid-hd-split",
34320     
34321         init: function(grid){
34322         this.grid = grid;
34323                 var cid = this.grid.getGridEl().id;
34324         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
34325         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
34326         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
34327         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
34328         },
34329         
34330         getColumnRenderers : function(){
34331         var renderers = [];
34332         var cm = this.grid.colModel;
34333         var colCount = cm.getColumnCount();
34334         for(var i = 0; i < colCount; i++){
34335             renderers[i] = cm.getRenderer(i);
34336         }
34337         return renderers;
34338     },
34339     
34340     getColumnIds : function(){
34341         var ids = [];
34342         var cm = this.grid.colModel;
34343         var colCount = cm.getColumnCount();
34344         for(var i = 0; i < colCount; i++){
34345             ids[i] = cm.getColumnId(i);
34346         }
34347         return ids;
34348     },
34349     
34350     getDataIndexes : function(){
34351         if(!this.indexMap){
34352             this.indexMap = this.buildIndexMap();
34353         }
34354         return this.indexMap.colToData;
34355     },
34356     
34357     getColumnIndexByDataIndex : function(dataIndex){
34358         if(!this.indexMap){
34359             this.indexMap = this.buildIndexMap();
34360         }
34361         return this.indexMap.dataToCol[dataIndex];
34362     },
34363     
34364     /**
34365      * Set a css style for a column dynamically. 
34366      * @param {Number} colIndex The index of the column
34367      * @param {String} name The css property name
34368      * @param {String} value The css value
34369      */
34370     setCSSStyle : function(colIndex, name, value){
34371         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
34372         Roo.util.CSS.updateRule(selector, name, value);
34373     },
34374     
34375     generateRules : function(cm){
34376         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
34377         Roo.util.CSS.removeStyleSheet(rulesId);
34378         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34379             var cid = cm.getColumnId(i);
34380             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
34381                          this.tdSelector, cid, " {\n}\n",
34382                          this.hdSelector, cid, " {\n}\n",
34383                          this.splitSelector, cid, " {\n}\n");
34384         }
34385         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34386     }
34387 });/*
34388  * Based on:
34389  * Ext JS Library 1.1.1
34390  * Copyright(c) 2006-2007, Ext JS, LLC.
34391  *
34392  * Originally Released Under LGPL - original licence link has changed is not relivant.
34393  *
34394  * Fork - LGPL
34395  * <script type="text/javascript">
34396  */
34397
34398 // private
34399 // This is a support class used internally by the Grid components
34400 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
34401     this.grid = grid;
34402     this.view = grid.getView();
34403     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34404     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
34405     if(hd2){
34406         this.setHandleElId(Roo.id(hd));
34407         this.setOuterHandleElId(Roo.id(hd2));
34408     }
34409     this.scroll = false;
34410 };
34411 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
34412     maxDragWidth: 120,
34413     getDragData : function(e){
34414         var t = Roo.lib.Event.getTarget(e);
34415         var h = this.view.findHeaderCell(t);
34416         if(h){
34417             return {ddel: h.firstChild, header:h};
34418         }
34419         return false;
34420     },
34421
34422     onInitDrag : function(e){
34423         this.view.headersDisabled = true;
34424         var clone = this.dragData.ddel.cloneNode(true);
34425         clone.id = Roo.id();
34426         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
34427         this.proxy.update(clone);
34428         return true;
34429     },
34430
34431     afterValidDrop : function(){
34432         var v = this.view;
34433         setTimeout(function(){
34434             v.headersDisabled = false;
34435         }, 50);
34436     },
34437
34438     afterInvalidDrop : function(){
34439         var v = this.view;
34440         setTimeout(function(){
34441             v.headersDisabled = false;
34442         }, 50);
34443     }
34444 });
34445 /*
34446  * Based on:
34447  * Ext JS Library 1.1.1
34448  * Copyright(c) 2006-2007, Ext JS, LLC.
34449  *
34450  * Originally Released Under LGPL - original licence link has changed is not relivant.
34451  *
34452  * Fork - LGPL
34453  * <script type="text/javascript">
34454  */
34455 // private
34456 // This is a support class used internally by the Grid components
34457 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
34458     this.grid = grid;
34459     this.view = grid.getView();
34460     // split the proxies so they don't interfere with mouse events
34461     this.proxyTop = Roo.DomHelper.append(document.body, {
34462         cls:"col-move-top", html:"&#160;"
34463     }, true);
34464     this.proxyBottom = Roo.DomHelper.append(document.body, {
34465         cls:"col-move-bottom", html:"&#160;"
34466     }, true);
34467     this.proxyTop.hide = this.proxyBottom.hide = function(){
34468         this.setLeftTop(-100,-100);
34469         this.setStyle("visibility", "hidden");
34470     };
34471     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34472     // temporarily disabled
34473     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
34474     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
34475 };
34476 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
34477     proxyOffsets : [-4, -9],
34478     fly: Roo.Element.fly,
34479
34480     getTargetFromEvent : function(e){
34481         var t = Roo.lib.Event.getTarget(e);
34482         var cindex = this.view.findCellIndex(t);
34483         if(cindex !== false){
34484             return this.view.getHeaderCell(cindex);
34485         }
34486         return null;
34487     },
34488
34489     nextVisible : function(h){
34490         var v = this.view, cm = this.grid.colModel;
34491         h = h.nextSibling;
34492         while(h){
34493             if(!cm.isHidden(v.getCellIndex(h))){
34494                 return h;
34495             }
34496             h = h.nextSibling;
34497         }
34498         return null;
34499     },
34500
34501     prevVisible : function(h){
34502         var v = this.view, cm = this.grid.colModel;
34503         h = h.prevSibling;
34504         while(h){
34505             if(!cm.isHidden(v.getCellIndex(h))){
34506                 return h;
34507             }
34508             h = h.prevSibling;
34509         }
34510         return null;
34511     },
34512
34513     positionIndicator : function(h, n, e){
34514         var x = Roo.lib.Event.getPageX(e);
34515         var r = Roo.lib.Dom.getRegion(n.firstChild);
34516         var px, pt, py = r.top + this.proxyOffsets[1];
34517         if((r.right - x) <= (r.right-r.left)/2){
34518             px = r.right+this.view.borderWidth;
34519             pt = "after";
34520         }else{
34521             px = r.left;
34522             pt = "before";
34523         }
34524         var oldIndex = this.view.getCellIndex(h);
34525         var newIndex = this.view.getCellIndex(n);
34526
34527         if(this.grid.colModel.isFixed(newIndex)){
34528             return false;
34529         }
34530
34531         var locked = this.grid.colModel.isLocked(newIndex);
34532
34533         if(pt == "after"){
34534             newIndex++;
34535         }
34536         if(oldIndex < newIndex){
34537             newIndex--;
34538         }
34539         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
34540             return false;
34541         }
34542         px +=  this.proxyOffsets[0];
34543         this.proxyTop.setLeftTop(px, py);
34544         this.proxyTop.show();
34545         if(!this.bottomOffset){
34546             this.bottomOffset = this.view.mainHd.getHeight();
34547         }
34548         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
34549         this.proxyBottom.show();
34550         return pt;
34551     },
34552
34553     onNodeEnter : function(n, dd, e, data){
34554         if(data.header != n){
34555             this.positionIndicator(data.header, n, e);
34556         }
34557     },
34558
34559     onNodeOver : function(n, dd, e, data){
34560         var result = false;
34561         if(data.header != n){
34562             result = this.positionIndicator(data.header, n, e);
34563         }
34564         if(!result){
34565             this.proxyTop.hide();
34566             this.proxyBottom.hide();
34567         }
34568         return result ? this.dropAllowed : this.dropNotAllowed;
34569     },
34570
34571     onNodeOut : function(n, dd, e, data){
34572         this.proxyTop.hide();
34573         this.proxyBottom.hide();
34574     },
34575
34576     onNodeDrop : function(n, dd, e, data){
34577         var h = data.header;
34578         if(h != n){
34579             var cm = this.grid.colModel;
34580             var x = Roo.lib.Event.getPageX(e);
34581             var r = Roo.lib.Dom.getRegion(n.firstChild);
34582             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
34583             var oldIndex = this.view.getCellIndex(h);
34584             var newIndex = this.view.getCellIndex(n);
34585             var locked = cm.isLocked(newIndex);
34586             if(pt == "after"){
34587                 newIndex++;
34588             }
34589             if(oldIndex < newIndex){
34590                 newIndex--;
34591             }
34592             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
34593                 return false;
34594             }
34595             cm.setLocked(oldIndex, locked, true);
34596             cm.moveColumn(oldIndex, newIndex);
34597             this.grid.fireEvent("columnmove", oldIndex, newIndex);
34598             return true;
34599         }
34600         return false;
34601     }
34602 });
34603 /*
34604  * Based on:
34605  * Ext JS Library 1.1.1
34606  * Copyright(c) 2006-2007, Ext JS, LLC.
34607  *
34608  * Originally Released Under LGPL - original licence link has changed is not relivant.
34609  *
34610  * Fork - LGPL
34611  * <script type="text/javascript">
34612  */
34613   
34614 /**
34615  * @class Roo.grid.GridView
34616  * @extends Roo.util.Observable
34617  *
34618  * @constructor
34619  * @param {Object} config
34620  */
34621 Roo.grid.GridView = function(config){
34622     Roo.grid.GridView.superclass.constructor.call(this);
34623     this.el = null;
34624
34625     Roo.apply(this, config);
34626 };
34627
34628 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
34629
34630     
34631     rowClass : "x-grid-row",
34632
34633     cellClass : "x-grid-col",
34634
34635     tdClass : "x-grid-td",
34636
34637     hdClass : "x-grid-hd",
34638
34639     splitClass : "x-grid-split",
34640
34641     sortClasses : ["sort-asc", "sort-desc"],
34642
34643     enableMoveAnim : false,
34644
34645     hlColor: "C3DAF9",
34646
34647     dh : Roo.DomHelper,
34648
34649     fly : Roo.Element.fly,
34650
34651     css : Roo.util.CSS,
34652
34653     borderWidth: 1,
34654
34655     splitOffset: 3,
34656
34657     scrollIncrement : 22,
34658
34659     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
34660
34661     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
34662
34663     bind : function(ds, cm){
34664         if(this.ds){
34665             this.ds.un("load", this.onLoad, this);
34666             this.ds.un("datachanged", this.onDataChange, this);
34667             this.ds.un("add", this.onAdd, this);
34668             this.ds.un("remove", this.onRemove, this);
34669             this.ds.un("update", this.onUpdate, this);
34670             this.ds.un("clear", this.onClear, this);
34671         }
34672         if(ds){
34673             ds.on("load", this.onLoad, this);
34674             ds.on("datachanged", this.onDataChange, this);
34675             ds.on("add", this.onAdd, this);
34676             ds.on("remove", this.onRemove, this);
34677             ds.on("update", this.onUpdate, this);
34678             ds.on("clear", this.onClear, this);
34679         }
34680         this.ds = ds;
34681
34682         if(this.cm){
34683             this.cm.un("widthchange", this.onColWidthChange, this);
34684             this.cm.un("headerchange", this.onHeaderChange, this);
34685             this.cm.un("hiddenchange", this.onHiddenChange, this);
34686             this.cm.un("columnmoved", this.onColumnMove, this);
34687             this.cm.un("columnlockchange", this.onColumnLock, this);
34688         }
34689         if(cm){
34690             this.generateRules(cm);
34691             cm.on("widthchange", this.onColWidthChange, this);
34692             cm.on("headerchange", this.onHeaderChange, this);
34693             cm.on("hiddenchange", this.onHiddenChange, this);
34694             cm.on("columnmoved", this.onColumnMove, this);
34695             cm.on("columnlockchange", this.onColumnLock, this);
34696         }
34697         this.cm = cm;
34698     },
34699
34700     init: function(grid){
34701         Roo.grid.GridView.superclass.init.call(this, grid);
34702
34703         this.bind(grid.dataSource, grid.colModel);
34704
34705         grid.on("headerclick", this.handleHeaderClick, this);
34706
34707         if(grid.trackMouseOver){
34708             grid.on("mouseover", this.onRowOver, this);
34709             grid.on("mouseout", this.onRowOut, this);
34710         }
34711         grid.cancelTextSelection = function(){};
34712         this.gridId = grid.id;
34713
34714         var tpls = this.templates || {};
34715
34716         if(!tpls.master){
34717             tpls.master = new Roo.Template(
34718                '<div class="x-grid" hidefocus="true">',
34719                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
34720                   '<div class="x-grid-topbar"></div>',
34721                   '<div class="x-grid-scroller"><div></div></div>',
34722                   '<div class="x-grid-locked">',
34723                       '<div class="x-grid-header">{lockedHeader}</div>',
34724                       '<div class="x-grid-body">{lockedBody}</div>',
34725                   "</div>",
34726                   '<div class="x-grid-viewport">',
34727                       '<div class="x-grid-header">{header}</div>',
34728                       '<div class="x-grid-body">{body}</div>',
34729                   "</div>",
34730                   '<div class="x-grid-bottombar"></div>',
34731                  
34732                   '<div class="x-grid-resize-proxy">&#160;</div>',
34733                "</div>"
34734             );
34735             tpls.master.disableformats = true;
34736         }
34737
34738         if(!tpls.header){
34739             tpls.header = new Roo.Template(
34740                '<table border="0" cellspacing="0" cellpadding="0">',
34741                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
34742                "</table>{splits}"
34743             );
34744             tpls.header.disableformats = true;
34745         }
34746         tpls.header.compile();
34747
34748         if(!tpls.hcell){
34749             tpls.hcell = new Roo.Template(
34750                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
34751                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
34752                 "</div></td>"
34753              );
34754              tpls.hcell.disableFormats = true;
34755         }
34756         tpls.hcell.compile();
34757
34758         if(!tpls.hsplit){
34759             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
34760             tpls.hsplit.disableFormats = true;
34761         }
34762         tpls.hsplit.compile();
34763
34764         if(!tpls.body){
34765             tpls.body = new Roo.Template(
34766                '<table border="0" cellspacing="0" cellpadding="0">',
34767                "<tbody>{rows}</tbody>",
34768                "</table>"
34769             );
34770             tpls.body.disableFormats = true;
34771         }
34772         tpls.body.compile();
34773
34774         if(!tpls.row){
34775             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
34776             tpls.row.disableFormats = true;
34777         }
34778         tpls.row.compile();
34779
34780         if(!tpls.cell){
34781             tpls.cell = new Roo.Template(
34782                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
34783                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
34784                 "</td>"
34785             );
34786             tpls.cell.disableFormats = true;
34787         }
34788         tpls.cell.compile();
34789
34790         this.templates = tpls;
34791     },
34792
34793     // remap these for backwards compat
34794     onColWidthChange : function(){
34795         this.updateColumns.apply(this, arguments);
34796     },
34797     onHeaderChange : function(){
34798         this.updateHeaders.apply(this, arguments);
34799     }, 
34800     onHiddenChange : function(){
34801         this.handleHiddenChange.apply(this, arguments);
34802     },
34803     onColumnMove : function(){
34804         this.handleColumnMove.apply(this, arguments);
34805     },
34806     onColumnLock : function(){
34807         this.handleLockChange.apply(this, arguments);
34808     },
34809
34810     onDataChange : function(){
34811         this.refresh();
34812         this.updateHeaderSortState();
34813     },
34814
34815     onClear : function(){
34816         this.refresh();
34817     },
34818
34819     onUpdate : function(ds, record){
34820         this.refreshRow(record);
34821     },
34822
34823     refreshRow : function(record){
34824         var ds = this.ds, index;
34825         if(typeof record == 'number'){
34826             index = record;
34827             record = ds.getAt(index);
34828         }else{
34829             index = ds.indexOf(record);
34830         }
34831         this.insertRows(ds, index, index, true);
34832         this.onRemove(ds, record, index+1, true);
34833         this.syncRowHeights(index, index);
34834         this.layout();
34835         this.fireEvent("rowupdated", this, index, record);
34836     },
34837
34838     onAdd : function(ds, records, index){
34839         this.insertRows(ds, index, index + (records.length-1));
34840     },
34841
34842     onRemove : function(ds, record, index, isUpdate){
34843         if(isUpdate !== true){
34844             this.fireEvent("beforerowremoved", this, index, record);
34845         }
34846         var bt = this.getBodyTable(), lt = this.getLockedTable();
34847         if(bt.rows[index]){
34848             bt.firstChild.removeChild(bt.rows[index]);
34849         }
34850         if(lt.rows[index]){
34851             lt.firstChild.removeChild(lt.rows[index]);
34852         }
34853         if(isUpdate !== true){
34854             this.stripeRows(index);
34855             this.syncRowHeights(index, index);
34856             this.layout();
34857             this.fireEvent("rowremoved", this, index, record);
34858         }
34859     },
34860
34861     onLoad : function(){
34862         this.scrollToTop();
34863     },
34864
34865     /**
34866      * Scrolls the grid to the top
34867      */
34868     scrollToTop : function(){
34869         if(this.scroller){
34870             this.scroller.dom.scrollTop = 0;
34871             this.syncScroll();
34872         }
34873     },
34874
34875     /**
34876      * Gets a panel in the header of the grid that can be used for toolbars etc.
34877      * After modifying the contents of this panel a call to grid.autoSize() may be
34878      * required to register any changes in size.
34879      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
34880      * @return Roo.Element
34881      */
34882     getHeaderPanel : function(doShow){
34883         if(doShow){
34884             this.headerPanel.show();
34885         }
34886         return this.headerPanel;
34887     },
34888
34889     /**
34890      * Gets a panel in the footer of the grid that can be used for toolbars etc.
34891      * After modifying the contents of this panel a call to grid.autoSize() may be
34892      * required to register any changes in size.
34893      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
34894      * @return Roo.Element
34895      */
34896     getFooterPanel : function(doShow){
34897         if(doShow){
34898             this.footerPanel.show();
34899         }
34900         return this.footerPanel;
34901     },
34902
34903     initElements : function(){
34904         var E = Roo.Element;
34905         var el = this.grid.getGridEl().dom.firstChild;
34906         var cs = el.childNodes;
34907
34908         this.el = new E(el);
34909         
34910          this.focusEl = new E(el.firstChild);
34911         this.focusEl.swallowEvent("click", true);
34912         
34913         this.headerPanel = new E(cs[1]);
34914         this.headerPanel.enableDisplayMode("block");
34915
34916         this.scroller = new E(cs[2]);
34917         this.scrollSizer = new E(this.scroller.dom.firstChild);
34918
34919         this.lockedWrap = new E(cs[3]);
34920         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
34921         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
34922
34923         this.mainWrap = new E(cs[4]);
34924         this.mainHd = new E(this.mainWrap.dom.firstChild);
34925         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
34926
34927         this.footerPanel = new E(cs[5]);
34928         this.footerPanel.enableDisplayMode("block");
34929
34930         this.resizeProxy = new E(cs[6]);
34931
34932         this.headerSelector = String.format(
34933            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
34934            this.lockedHd.id, this.mainHd.id
34935         );
34936
34937         this.splitterSelector = String.format(
34938            '#{0} div.x-grid-split, #{1} div.x-grid-split',
34939            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
34940         );
34941     },
34942     idToCssName : function(s)
34943     {
34944         return s.replace(/[^a-z0-9]+/ig, '-');
34945     },
34946
34947     getHeaderCell : function(index){
34948         return Roo.DomQuery.select(this.headerSelector)[index];
34949     },
34950
34951     getHeaderCellMeasure : function(index){
34952         return this.getHeaderCell(index).firstChild;
34953     },
34954
34955     getHeaderCellText : function(index){
34956         return this.getHeaderCell(index).firstChild.firstChild;
34957     },
34958
34959     getLockedTable : function(){
34960         return this.lockedBody.dom.firstChild;
34961     },
34962
34963     getBodyTable : function(){
34964         return this.mainBody.dom.firstChild;
34965     },
34966
34967     getLockedRow : function(index){
34968         return this.getLockedTable().rows[index];
34969     },
34970
34971     getRow : function(index){
34972         return this.getBodyTable().rows[index];
34973     },
34974
34975     getRowComposite : function(index){
34976         if(!this.rowEl){
34977             this.rowEl = new Roo.CompositeElementLite();
34978         }
34979         var els = [], lrow, mrow;
34980         if(lrow = this.getLockedRow(index)){
34981             els.push(lrow);
34982         }
34983         if(mrow = this.getRow(index)){
34984             els.push(mrow);
34985         }
34986         this.rowEl.elements = els;
34987         return this.rowEl;
34988     },
34989     /**
34990      * Gets the 'td' of the cell
34991      * 
34992      * @param {Integer} rowIndex row to select
34993      * @param {Integer} colIndex column to select
34994      * 
34995      * @return {Object} 
34996      */
34997     getCell : function(rowIndex, colIndex){
34998         var locked = this.cm.getLockedCount();
34999         var source;
35000         if(colIndex < locked){
35001             source = this.lockedBody.dom.firstChild;
35002         }else{
35003             source = this.mainBody.dom.firstChild;
35004             colIndex -= locked;
35005         }
35006         return source.rows[rowIndex].childNodes[colIndex];
35007     },
35008
35009     getCellText : function(rowIndex, colIndex){
35010         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
35011     },
35012
35013     getCellBox : function(cell){
35014         var b = this.fly(cell).getBox();
35015         if(Roo.isOpera){ // opera fails to report the Y
35016             b.y = cell.offsetTop + this.mainBody.getY();
35017         }
35018         return b;
35019     },
35020
35021     getCellIndex : function(cell){
35022         var id = String(cell.className).match(this.cellRE);
35023         if(id){
35024             return parseInt(id[1], 10);
35025         }
35026         return 0;
35027     },
35028
35029     findHeaderIndex : function(n){
35030         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35031         return r ? this.getCellIndex(r) : false;
35032     },
35033
35034     findHeaderCell : function(n){
35035         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35036         return r ? r : false;
35037     },
35038
35039     findRowIndex : function(n){
35040         if(!n){
35041             return false;
35042         }
35043         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
35044         return r ? r.rowIndex : false;
35045     },
35046
35047     findCellIndex : function(node){
35048         var stop = this.el.dom;
35049         while(node && node != stop){
35050             if(this.findRE.test(node.className)){
35051                 return this.getCellIndex(node);
35052             }
35053             node = node.parentNode;
35054         }
35055         return false;
35056     },
35057
35058     getColumnId : function(index){
35059         return this.cm.getColumnId(index);
35060     },
35061
35062     getSplitters : function()
35063     {
35064         if(this.splitterSelector){
35065            return Roo.DomQuery.select(this.splitterSelector);
35066         }else{
35067             return null;
35068       }
35069     },
35070
35071     getSplitter : function(index){
35072         return this.getSplitters()[index];
35073     },
35074
35075     onRowOver : function(e, t){
35076         var row;
35077         if((row = this.findRowIndex(t)) !== false){
35078             this.getRowComposite(row).addClass("x-grid-row-over");
35079         }
35080     },
35081
35082     onRowOut : function(e, t){
35083         var row;
35084         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
35085             this.getRowComposite(row).removeClass("x-grid-row-over");
35086         }
35087     },
35088
35089     renderHeaders : function(){
35090         var cm = this.cm;
35091         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
35092         var cb = [], lb = [], sb = [], lsb = [], p = {};
35093         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35094             p.cellId = "x-grid-hd-0-" + i;
35095             p.splitId = "x-grid-csplit-0-" + i;
35096             p.id = cm.getColumnId(i);
35097             p.title = cm.getColumnTooltip(i) || "";
35098             p.value = cm.getColumnHeader(i) || "";
35099             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
35100             if(!cm.isLocked(i)){
35101                 cb[cb.length] = ct.apply(p);
35102                 sb[sb.length] = st.apply(p);
35103             }else{
35104                 lb[lb.length] = ct.apply(p);
35105                 lsb[lsb.length] = st.apply(p);
35106             }
35107         }
35108         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
35109                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
35110     },
35111
35112     updateHeaders : function(){
35113         var html = this.renderHeaders();
35114         this.lockedHd.update(html[0]);
35115         this.mainHd.update(html[1]);
35116     },
35117
35118     /**
35119      * Focuses the specified row.
35120      * @param {Number} row The row index
35121      */
35122     focusRow : function(row)
35123     {
35124         //Roo.log('GridView.focusRow');
35125         var x = this.scroller.dom.scrollLeft;
35126         this.focusCell(row, 0, false);
35127         this.scroller.dom.scrollLeft = x;
35128     },
35129
35130     /**
35131      * Focuses the specified cell.
35132      * @param {Number} row The row index
35133      * @param {Number} col The column index
35134      * @param {Boolean} hscroll false to disable horizontal scrolling
35135      */
35136     focusCell : function(row, col, hscroll)
35137     {
35138         //Roo.log('GridView.focusCell');
35139         var el = this.ensureVisible(row, col, hscroll);
35140         this.focusEl.alignTo(el, "tl-tl");
35141         if(Roo.isGecko){
35142             this.focusEl.focus();
35143         }else{
35144             this.focusEl.focus.defer(1, this.focusEl);
35145         }
35146     },
35147
35148     /**
35149      * Scrolls the specified cell into view
35150      * @param {Number} row The row index
35151      * @param {Number} col The column index
35152      * @param {Boolean} hscroll false to disable horizontal scrolling
35153      */
35154     ensureVisible : function(row, col, hscroll)
35155     {
35156         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
35157         //return null; //disable for testing.
35158         if(typeof row != "number"){
35159             row = row.rowIndex;
35160         }
35161         if(row < 0 && row >= this.ds.getCount()){
35162             return  null;
35163         }
35164         col = (col !== undefined ? col : 0);
35165         var cm = this.grid.colModel;
35166         while(cm.isHidden(col)){
35167             col++;
35168         }
35169
35170         var el = this.getCell(row, col);
35171         if(!el){
35172             return null;
35173         }
35174         var c = this.scroller.dom;
35175
35176         var ctop = parseInt(el.offsetTop, 10);
35177         var cleft = parseInt(el.offsetLeft, 10);
35178         var cbot = ctop + el.offsetHeight;
35179         var cright = cleft + el.offsetWidth;
35180         
35181         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
35182         var stop = parseInt(c.scrollTop, 10);
35183         var sleft = parseInt(c.scrollLeft, 10);
35184         var sbot = stop + ch;
35185         var sright = sleft + c.clientWidth;
35186         /*
35187         Roo.log('GridView.ensureVisible:' +
35188                 ' ctop:' + ctop +
35189                 ' c.clientHeight:' + c.clientHeight +
35190                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
35191                 ' stop:' + stop +
35192                 ' cbot:' + cbot +
35193                 ' sbot:' + sbot +
35194                 ' ch:' + ch  
35195                 );
35196         */
35197         if(ctop < stop){
35198              c.scrollTop = ctop;
35199             //Roo.log("set scrolltop to ctop DISABLE?");
35200         }else if(cbot > sbot){
35201             //Roo.log("set scrolltop to cbot-ch");
35202             c.scrollTop = cbot-ch;
35203         }
35204         
35205         if(hscroll !== false){
35206             if(cleft < sleft){
35207                 c.scrollLeft = cleft;
35208             }else if(cright > sright){
35209                 c.scrollLeft = cright-c.clientWidth;
35210             }
35211         }
35212          
35213         return el;
35214     },
35215
35216     updateColumns : function(){
35217         this.grid.stopEditing();
35218         var cm = this.grid.colModel, colIds = this.getColumnIds();
35219         //var totalWidth = cm.getTotalWidth();
35220         var pos = 0;
35221         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35222             //if(cm.isHidden(i)) continue;
35223             var w = cm.getColumnWidth(i);
35224             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35225             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35226         }
35227         this.updateSplitters();
35228     },
35229
35230     generateRules : function(cm){
35231         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
35232         Roo.util.CSS.removeStyleSheet(rulesId);
35233         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35234             var cid = cm.getColumnId(i);
35235             var align = '';
35236             if(cm.config[i].align){
35237                 align = 'text-align:'+cm.config[i].align+';';
35238             }
35239             var hidden = '';
35240             if(cm.isHidden(i)){
35241                 hidden = 'display:none;';
35242             }
35243             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
35244             ruleBuf.push(
35245                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
35246                     this.hdSelector, cid, " {\n", align, width, "}\n",
35247                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
35248                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
35249         }
35250         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35251     },
35252
35253     updateSplitters : function(){
35254         var cm = this.cm, s = this.getSplitters();
35255         if(s){ // splitters not created yet
35256             var pos = 0, locked = true;
35257             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35258                 if(cm.isHidden(i)) continue;
35259                 var w = cm.getColumnWidth(i); // make sure it's a number
35260                 if(!cm.isLocked(i) && locked){
35261                     pos = 0;
35262                     locked = false;
35263                 }
35264                 pos += w;
35265                 s[i].style.left = (pos-this.splitOffset) + "px";
35266             }
35267         }
35268     },
35269
35270     handleHiddenChange : function(colModel, colIndex, hidden){
35271         if(hidden){
35272             this.hideColumn(colIndex);
35273         }else{
35274             this.unhideColumn(colIndex);
35275         }
35276     },
35277
35278     hideColumn : function(colIndex){
35279         var cid = this.getColumnId(colIndex);
35280         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
35281         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
35282         if(Roo.isSafari){
35283             this.updateHeaders();
35284         }
35285         this.updateSplitters();
35286         this.layout();
35287     },
35288
35289     unhideColumn : function(colIndex){
35290         var cid = this.getColumnId(colIndex);
35291         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
35292         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
35293
35294         if(Roo.isSafari){
35295             this.updateHeaders();
35296         }
35297         this.updateSplitters();
35298         this.layout();
35299     },
35300
35301     insertRows : function(dm, firstRow, lastRow, isUpdate){
35302         if(firstRow == 0 && lastRow == dm.getCount()-1){
35303             this.refresh();
35304         }else{
35305             if(!isUpdate){
35306                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
35307             }
35308             var s = this.getScrollState();
35309             var markup = this.renderRows(firstRow, lastRow);
35310             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
35311             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
35312             this.restoreScroll(s);
35313             if(!isUpdate){
35314                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
35315                 this.syncRowHeights(firstRow, lastRow);
35316                 this.stripeRows(firstRow);
35317                 this.layout();
35318             }
35319         }
35320     },
35321
35322     bufferRows : function(markup, target, index){
35323         var before = null, trows = target.rows, tbody = target.tBodies[0];
35324         if(index < trows.length){
35325             before = trows[index];
35326         }
35327         var b = document.createElement("div");
35328         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
35329         var rows = b.firstChild.rows;
35330         for(var i = 0, len = rows.length; i < len; i++){
35331             if(before){
35332                 tbody.insertBefore(rows[0], before);
35333             }else{
35334                 tbody.appendChild(rows[0]);
35335             }
35336         }
35337         b.innerHTML = "";
35338         b = null;
35339     },
35340
35341     deleteRows : function(dm, firstRow, lastRow){
35342         if(dm.getRowCount()<1){
35343             this.fireEvent("beforerefresh", this);
35344             this.mainBody.update("");
35345             this.lockedBody.update("");
35346             this.fireEvent("refresh", this);
35347         }else{
35348             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
35349             var bt = this.getBodyTable();
35350             var tbody = bt.firstChild;
35351             var rows = bt.rows;
35352             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
35353                 tbody.removeChild(rows[firstRow]);
35354             }
35355             this.stripeRows(firstRow);
35356             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
35357         }
35358     },
35359
35360     updateRows : function(dataSource, firstRow, lastRow){
35361         var s = this.getScrollState();
35362         this.refresh();
35363         this.restoreScroll(s);
35364     },
35365
35366     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
35367         if(!noRefresh){
35368            this.refresh();
35369         }
35370         this.updateHeaderSortState();
35371     },
35372
35373     getScrollState : function(){
35374         
35375         var sb = this.scroller.dom;
35376         return {left: sb.scrollLeft, top: sb.scrollTop};
35377     },
35378
35379     stripeRows : function(startRow){
35380         if(!this.grid.stripeRows || this.ds.getCount() < 1){
35381             return;
35382         }
35383         startRow = startRow || 0;
35384         var rows = this.getBodyTable().rows;
35385         var lrows = this.getLockedTable().rows;
35386         var cls = ' x-grid-row-alt ';
35387         for(var i = startRow, len = rows.length; i < len; i++){
35388             var row = rows[i], lrow = lrows[i];
35389             var isAlt = ((i+1) % 2 == 0);
35390             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
35391             if(isAlt == hasAlt){
35392                 continue;
35393             }
35394             if(isAlt){
35395                 row.className += " x-grid-row-alt";
35396             }else{
35397                 row.className = row.className.replace("x-grid-row-alt", "");
35398             }
35399             if(lrow){
35400                 lrow.className = row.className;
35401             }
35402         }
35403     },
35404
35405     restoreScroll : function(state){
35406         //Roo.log('GridView.restoreScroll');
35407         var sb = this.scroller.dom;
35408         sb.scrollLeft = state.left;
35409         sb.scrollTop = state.top;
35410         this.syncScroll();
35411     },
35412
35413     syncScroll : function(){
35414         //Roo.log('GridView.syncScroll');
35415         var sb = this.scroller.dom;
35416         var sh = this.mainHd.dom;
35417         var bs = this.mainBody.dom;
35418         var lv = this.lockedBody.dom;
35419         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
35420         lv.scrollTop = bs.scrollTop = sb.scrollTop;
35421     },
35422
35423     handleScroll : function(e){
35424         this.syncScroll();
35425         var sb = this.scroller.dom;
35426         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
35427         e.stopEvent();
35428     },
35429
35430     handleWheel : function(e){
35431         var d = e.getWheelDelta();
35432         this.scroller.dom.scrollTop -= d*22;
35433         // set this here to prevent jumpy scrolling on large tables
35434         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
35435         e.stopEvent();
35436     },
35437
35438     renderRows : function(startRow, endRow){
35439         // pull in all the crap needed to render rows
35440         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
35441         var colCount = cm.getColumnCount();
35442
35443         if(ds.getCount() < 1){
35444             return ["", ""];
35445         }
35446
35447         // build a map for all the columns
35448         var cs = [];
35449         for(var i = 0; i < colCount; i++){
35450             var name = cm.getDataIndex(i);
35451             cs[i] = {
35452                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
35453                 renderer : cm.getRenderer(i),
35454                 id : cm.getColumnId(i),
35455                 locked : cm.isLocked(i)
35456             };
35457         }
35458
35459         startRow = startRow || 0;
35460         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
35461
35462         // records to render
35463         var rs = ds.getRange(startRow, endRow);
35464
35465         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
35466     },
35467
35468     // As much as I hate to duplicate code, this was branched because FireFox really hates
35469     // [].join("") on strings. The performance difference was substantial enough to
35470     // branch this function
35471     doRender : Roo.isGecko ?
35472             function(cs, rs, ds, startRow, colCount, stripe){
35473                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35474                 // buffers
35475                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35476                 
35477                 var hasListener = this.grid.hasListener('rowclass');
35478                 var rowcfg = {};
35479                 for(var j = 0, len = rs.length; j < len; j++){
35480                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
35481                     for(var i = 0; i < colCount; i++){
35482                         c = cs[i];
35483                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35484                         p.id = c.id;
35485                         p.css = p.attr = "";
35486                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35487                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35488                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35489                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35490                         }
35491                         var markup = ct.apply(p);
35492                         if(!c.locked){
35493                             cb+= markup;
35494                         }else{
35495                             lcb+= markup;
35496                         }
35497                     }
35498                     var alt = [];
35499                     if(stripe && ((rowIndex+1) % 2 == 0)){
35500                         alt.push("x-grid-row-alt")
35501                     }
35502                     if(r.dirty){
35503                         alt.push(  " x-grid-dirty-row");
35504                     }
35505                     rp.cells = lcb;
35506                     if(this.getRowClass){
35507                         alt.push(this.getRowClass(r, rowIndex));
35508                     }
35509                     if (hasListener) {
35510                         rowcfg = {
35511                              
35512                             record: r,
35513                             rowIndex : rowIndex,
35514                             rowClass : ''
35515                         }
35516                         this.grid.fireEvent('rowclass', this, rowcfg);
35517                         alt.push(rowcfg.rowClass);
35518                     }
35519                     rp.alt = alt.join(" ");
35520                     lbuf+= rt.apply(rp);
35521                     rp.cells = cb;
35522                     buf+=  rt.apply(rp);
35523                 }
35524                 return [lbuf, buf];
35525             } :
35526             function(cs, rs, ds, startRow, colCount, stripe){
35527                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35528                 // buffers
35529                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35530                 var hasListener = this.grid.hasListener('rowclass');
35531  
35532                 var rowcfg = {};
35533                 for(var j = 0, len = rs.length; j < len; j++){
35534                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
35535                     for(var i = 0; i < colCount; i++){
35536                         c = cs[i];
35537                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35538                         p.id = c.id;
35539                         p.css = p.attr = "";
35540                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35541                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35542                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35543                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35544                         }
35545                         
35546                         var markup = ct.apply(p);
35547                         if(!c.locked){
35548                             cb[cb.length] = markup;
35549                         }else{
35550                             lcb[lcb.length] = markup;
35551                         }
35552                     }
35553                     var alt = [];
35554                     if(stripe && ((rowIndex+1) % 2 == 0)){
35555                         alt.push( "x-grid-row-alt");
35556                     }
35557                     if(r.dirty){
35558                         alt.push(" x-grid-dirty-row");
35559                     }
35560                     rp.cells = lcb;
35561                     if(this.getRowClass){
35562                         alt.push( this.getRowClass(r, rowIndex));
35563                     }
35564                     if (hasListener) {
35565                         rowcfg = {
35566                              
35567                             record: r,
35568                             rowIndex : rowIndex,
35569                             rowClass : ''
35570                         }
35571                         this.grid.fireEvent('rowclass', this, rowcfg);
35572                         alt.push(rowcfg.rowClass);
35573                     }
35574                     rp.alt = alt.join(" ");
35575                     rp.cells = lcb.join("");
35576                     lbuf[lbuf.length] = rt.apply(rp);
35577                     rp.cells = cb.join("");
35578                     buf[buf.length] =  rt.apply(rp);
35579                 }
35580                 return [lbuf.join(""), buf.join("")];
35581             },
35582
35583     renderBody : function(){
35584         var markup = this.renderRows();
35585         var bt = this.templates.body;
35586         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
35587     },
35588
35589     /**
35590      * Refreshes the grid
35591      * @param {Boolean} headersToo
35592      */
35593     refresh : function(headersToo){
35594         this.fireEvent("beforerefresh", this);
35595         this.grid.stopEditing();
35596         var result = this.renderBody();
35597         this.lockedBody.update(result[0]);
35598         this.mainBody.update(result[1]);
35599         if(headersToo === true){
35600             this.updateHeaders();
35601             this.updateColumns();
35602             this.updateSplitters();
35603             this.updateHeaderSortState();
35604         }
35605         this.syncRowHeights();
35606         this.layout();
35607         this.fireEvent("refresh", this);
35608     },
35609
35610     handleColumnMove : function(cm, oldIndex, newIndex){
35611         this.indexMap = null;
35612         var s = this.getScrollState();
35613         this.refresh(true);
35614         this.restoreScroll(s);
35615         this.afterMove(newIndex);
35616     },
35617
35618     afterMove : function(colIndex){
35619         if(this.enableMoveAnim && Roo.enableFx){
35620             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
35621         }
35622         // if multisort - fix sortOrder, and reload..
35623         if (this.grid.dataSource.multiSort) {
35624             // the we can call sort again..
35625             var dm = this.grid.dataSource;
35626             var cm = this.grid.colModel;
35627             var so = [];
35628             for(var i = 0; i < cm.config.length; i++ ) {
35629                 
35630                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
35631                     continue; // dont' bother, it's not in sort list or being set.
35632                 }
35633                 
35634                 so.push(cm.config[i].dataIndex);
35635             };
35636             dm.sortOrder = so;
35637             dm.load(dm.lastOptions);
35638             
35639             
35640         }
35641         
35642     },
35643
35644     updateCell : function(dm, rowIndex, dataIndex){
35645         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
35646         if(typeof colIndex == "undefined"){ // not present in grid
35647             return;
35648         }
35649         var cm = this.grid.colModel;
35650         var cell = this.getCell(rowIndex, colIndex);
35651         var cellText = this.getCellText(rowIndex, colIndex);
35652
35653         var p = {
35654             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
35655             id : cm.getColumnId(colIndex),
35656             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
35657         };
35658         var renderer = cm.getRenderer(colIndex);
35659         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
35660         if(typeof val == "undefined" || val === "") val = "&#160;";
35661         cellText.innerHTML = val;
35662         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
35663         this.syncRowHeights(rowIndex, rowIndex);
35664     },
35665
35666     calcColumnWidth : function(colIndex, maxRowsToMeasure){
35667         var maxWidth = 0;
35668         if(this.grid.autoSizeHeaders){
35669             var h = this.getHeaderCellMeasure(colIndex);
35670             maxWidth = Math.max(maxWidth, h.scrollWidth);
35671         }
35672         var tb, index;
35673         if(this.cm.isLocked(colIndex)){
35674             tb = this.getLockedTable();
35675             index = colIndex;
35676         }else{
35677             tb = this.getBodyTable();
35678             index = colIndex - this.cm.getLockedCount();
35679         }
35680         if(tb && tb.rows){
35681             var rows = tb.rows;
35682             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
35683             for(var i = 0; i < stopIndex; i++){
35684                 var cell = rows[i].childNodes[index].firstChild;
35685                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
35686             }
35687         }
35688         return maxWidth + /*margin for error in IE*/ 5;
35689     },
35690     /**
35691      * Autofit a column to its content.
35692      * @param {Number} colIndex
35693      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
35694      */
35695      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
35696          if(this.cm.isHidden(colIndex)){
35697              return; // can't calc a hidden column
35698          }
35699         if(forceMinSize){
35700             var cid = this.cm.getColumnId(colIndex);
35701             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
35702            if(this.grid.autoSizeHeaders){
35703                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
35704            }
35705         }
35706         var newWidth = this.calcColumnWidth(colIndex);
35707         this.cm.setColumnWidth(colIndex,
35708             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
35709         if(!suppressEvent){
35710             this.grid.fireEvent("columnresize", colIndex, newWidth);
35711         }
35712     },
35713
35714     /**
35715      * Autofits all columns to their content and then expands to fit any extra space in the grid
35716      */
35717      autoSizeColumns : function(){
35718         var cm = this.grid.colModel;
35719         var colCount = cm.getColumnCount();
35720         for(var i = 0; i < colCount; i++){
35721             this.autoSizeColumn(i, true, true);
35722         }
35723         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
35724             this.fitColumns();
35725         }else{
35726             this.updateColumns();
35727             this.layout();
35728         }
35729     },
35730
35731     /**
35732      * Autofits all columns to the grid's width proportionate with their current size
35733      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
35734      */
35735     fitColumns : function(reserveScrollSpace){
35736         var cm = this.grid.colModel;
35737         var colCount = cm.getColumnCount();
35738         var cols = [];
35739         var width = 0;
35740         var i, w;
35741         for (i = 0; i < colCount; i++){
35742             if(!cm.isHidden(i) && !cm.isFixed(i)){
35743                 w = cm.getColumnWidth(i);
35744                 cols.push(i);
35745                 cols.push(w);
35746                 width += w;
35747             }
35748         }
35749         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
35750         if(reserveScrollSpace){
35751             avail -= 17;
35752         }
35753         var frac = (avail - cm.getTotalWidth())/width;
35754         while (cols.length){
35755             w = cols.pop();
35756             i = cols.pop();
35757             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
35758         }
35759         this.updateColumns();
35760         this.layout();
35761     },
35762
35763     onRowSelect : function(rowIndex){
35764         var row = this.getRowComposite(rowIndex);
35765         row.addClass("x-grid-row-selected");
35766     },
35767
35768     onRowDeselect : function(rowIndex){
35769         var row = this.getRowComposite(rowIndex);
35770         row.removeClass("x-grid-row-selected");
35771     },
35772
35773     onCellSelect : function(row, col){
35774         var cell = this.getCell(row, col);
35775         if(cell){
35776             Roo.fly(cell).addClass("x-grid-cell-selected");
35777         }
35778     },
35779
35780     onCellDeselect : function(row, col){
35781         var cell = this.getCell(row, col);
35782         if(cell){
35783             Roo.fly(cell).removeClass("x-grid-cell-selected");
35784         }
35785     },
35786
35787     updateHeaderSortState : function(){
35788         
35789         // sort state can be single { field: xxx, direction : yyy}
35790         // or   { xxx=>ASC , yyy : DESC ..... }
35791         
35792         var mstate = {};
35793         if (!this.ds.multiSort) { 
35794             var state = this.ds.getSortState();
35795             if(!state){
35796                 return;
35797             }
35798             mstate[state.field] = state.direction;
35799             // FIXME... - this is not used here.. but might be elsewhere..
35800             this.sortState = state;
35801             
35802         } else {
35803             mstate = this.ds.sortToggle;
35804         }
35805         //remove existing sort classes..
35806         
35807         var sc = this.sortClasses;
35808         var hds = this.el.select(this.headerSelector).removeClass(sc);
35809         
35810         for(var f in mstate) {
35811         
35812             var sortColumn = this.cm.findColumnIndex(f);
35813             
35814             if(sortColumn != -1){
35815                 var sortDir = mstate[f];        
35816                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
35817             }
35818         }
35819         
35820          
35821         
35822     },
35823
35824
35825     handleHeaderClick : function(g, index){
35826         if(this.headersDisabled){
35827             return;
35828         }
35829         var dm = g.dataSource, cm = g.colModel;
35830         if(!cm.isSortable(index)){
35831             return;
35832         }
35833         g.stopEditing();
35834         
35835         if (dm.multiSort) {
35836             // update the sortOrder
35837             var so = [];
35838             for(var i = 0; i < cm.config.length; i++ ) {
35839                 
35840                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
35841                     continue; // dont' bother, it's not in sort list or being set.
35842                 }
35843                 
35844                 so.push(cm.config[i].dataIndex);
35845             };
35846             dm.sortOrder = so;
35847         }
35848         
35849         
35850         dm.sort(cm.getDataIndex(index));
35851     },
35852
35853
35854     destroy : function(){
35855         if(this.colMenu){
35856             this.colMenu.removeAll();
35857             Roo.menu.MenuMgr.unregister(this.colMenu);
35858             this.colMenu.getEl().remove();
35859             delete this.colMenu;
35860         }
35861         if(this.hmenu){
35862             this.hmenu.removeAll();
35863             Roo.menu.MenuMgr.unregister(this.hmenu);
35864             this.hmenu.getEl().remove();
35865             delete this.hmenu;
35866         }
35867         if(this.grid.enableColumnMove){
35868             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35869             if(dds){
35870                 for(var dd in dds){
35871                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
35872                         var elid = dds[dd].dragElId;
35873                         dds[dd].unreg();
35874                         Roo.get(elid).remove();
35875                     } else if(dds[dd].config.isTarget){
35876                         dds[dd].proxyTop.remove();
35877                         dds[dd].proxyBottom.remove();
35878                         dds[dd].unreg();
35879                     }
35880                     if(Roo.dd.DDM.locationCache[dd]){
35881                         delete Roo.dd.DDM.locationCache[dd];
35882                     }
35883                 }
35884                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35885             }
35886         }
35887         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
35888         this.bind(null, null);
35889         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
35890     },
35891
35892     handleLockChange : function(){
35893         this.refresh(true);
35894     },
35895
35896     onDenyColumnLock : function(){
35897
35898     },
35899
35900     onDenyColumnHide : function(){
35901
35902     },
35903
35904     handleHdMenuClick : function(item){
35905         var index = this.hdCtxIndex;
35906         var cm = this.cm, ds = this.ds;
35907         switch(item.id){
35908             case "asc":
35909                 ds.sort(cm.getDataIndex(index), "ASC");
35910                 break;
35911             case "desc":
35912                 ds.sort(cm.getDataIndex(index), "DESC");
35913                 break;
35914             case "lock":
35915                 var lc = cm.getLockedCount();
35916                 if(cm.getColumnCount(true) <= lc+1){
35917                     this.onDenyColumnLock();
35918                     return;
35919                 }
35920                 if(lc != index){
35921                     cm.setLocked(index, true, true);
35922                     cm.moveColumn(index, lc);
35923                     this.grid.fireEvent("columnmove", index, lc);
35924                 }else{
35925                     cm.setLocked(index, true);
35926                 }
35927             break;
35928             case "unlock":
35929                 var lc = cm.getLockedCount();
35930                 if((lc-1) != index){
35931                     cm.setLocked(index, false, true);
35932                     cm.moveColumn(index, lc-1);
35933                     this.grid.fireEvent("columnmove", index, lc-1);
35934                 }else{
35935                     cm.setLocked(index, false);
35936                 }
35937             break;
35938             default:
35939                 index = cm.getIndexById(item.id.substr(4));
35940                 if(index != -1){
35941                     if(item.checked && cm.getColumnCount(true) <= 1){
35942                         this.onDenyColumnHide();
35943                         return false;
35944                     }
35945                     cm.setHidden(index, item.checked);
35946                 }
35947         }
35948         return true;
35949     },
35950
35951     beforeColMenuShow : function(){
35952         var cm = this.cm,  colCount = cm.getColumnCount();
35953         this.colMenu.removeAll();
35954         for(var i = 0; i < colCount; i++){
35955             this.colMenu.add(new Roo.menu.CheckItem({
35956                 id: "col-"+cm.getColumnId(i),
35957                 text: cm.getColumnHeader(i),
35958                 checked: !cm.isHidden(i),
35959                 hideOnClick:false
35960             }));
35961         }
35962     },
35963
35964     handleHdCtx : function(g, index, e){
35965         e.stopEvent();
35966         var hd = this.getHeaderCell(index);
35967         this.hdCtxIndex = index;
35968         var ms = this.hmenu.items, cm = this.cm;
35969         ms.get("asc").setDisabled(!cm.isSortable(index));
35970         ms.get("desc").setDisabled(!cm.isSortable(index));
35971         if(this.grid.enableColLock !== false){
35972             ms.get("lock").setDisabled(cm.isLocked(index));
35973             ms.get("unlock").setDisabled(!cm.isLocked(index));
35974         }
35975         this.hmenu.show(hd, "tl-bl");
35976     },
35977
35978     handleHdOver : function(e){
35979         var hd = this.findHeaderCell(e.getTarget());
35980         if(hd && !this.headersDisabled){
35981             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
35982                this.fly(hd).addClass("x-grid-hd-over");
35983             }
35984         }
35985     },
35986
35987     handleHdOut : function(e){
35988         var hd = this.findHeaderCell(e.getTarget());
35989         if(hd){
35990             this.fly(hd).removeClass("x-grid-hd-over");
35991         }
35992     },
35993
35994     handleSplitDblClick : function(e, t){
35995         var i = this.getCellIndex(t);
35996         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
35997             this.autoSizeColumn(i, true);
35998             this.layout();
35999         }
36000     },
36001
36002     render : function(){
36003
36004         var cm = this.cm;
36005         var colCount = cm.getColumnCount();
36006
36007         if(this.grid.monitorWindowResize === true){
36008             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36009         }
36010         var header = this.renderHeaders();
36011         var body = this.templates.body.apply({rows:""});
36012         var html = this.templates.master.apply({
36013             lockedBody: body,
36014             body: body,
36015             lockedHeader: header[0],
36016             header: header[1]
36017         });
36018
36019         //this.updateColumns();
36020
36021         this.grid.getGridEl().dom.innerHTML = html;
36022
36023         this.initElements();
36024         
36025         // a kludge to fix the random scolling effect in webkit
36026         this.el.on("scroll", function() {
36027             this.el.dom.scrollTop=0; // hopefully not recursive..
36028         },this);
36029
36030         this.scroller.on("scroll", this.handleScroll, this);
36031         this.lockedBody.on("mousewheel", this.handleWheel, this);
36032         this.mainBody.on("mousewheel", this.handleWheel, this);
36033
36034         this.mainHd.on("mouseover", this.handleHdOver, this);
36035         this.mainHd.on("mouseout", this.handleHdOut, this);
36036         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
36037                 {delegate: "."+this.splitClass});
36038
36039         this.lockedHd.on("mouseover", this.handleHdOver, this);
36040         this.lockedHd.on("mouseout", this.handleHdOut, this);
36041         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
36042                 {delegate: "."+this.splitClass});
36043
36044         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
36045             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36046         }
36047
36048         this.updateSplitters();
36049
36050         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
36051             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36052             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36053         }
36054
36055         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
36056             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
36057             this.hmenu.add(
36058                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
36059                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
36060             );
36061             if(this.grid.enableColLock !== false){
36062                 this.hmenu.add('-',
36063                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
36064                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
36065                 );
36066             }
36067             if(this.grid.enableColumnHide !== false){
36068
36069                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
36070                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
36071                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
36072
36073                 this.hmenu.add('-',
36074                     {id:"columns", text: this.columnsText, menu: this.colMenu}
36075                 );
36076             }
36077             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
36078
36079             this.grid.on("headercontextmenu", this.handleHdCtx, this);
36080         }
36081
36082         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
36083             this.dd = new Roo.grid.GridDragZone(this.grid, {
36084                 ddGroup : this.grid.ddGroup || 'GridDD'
36085             });
36086         }
36087
36088         /*
36089         for(var i = 0; i < colCount; i++){
36090             if(cm.isHidden(i)){
36091                 this.hideColumn(i);
36092             }
36093             if(cm.config[i].align){
36094                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
36095                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
36096             }
36097         }*/
36098         
36099         this.updateHeaderSortState();
36100
36101         this.beforeInitialResize();
36102         this.layout(true);
36103
36104         // two part rendering gives faster view to the user
36105         this.renderPhase2.defer(1, this);
36106     },
36107
36108     renderPhase2 : function(){
36109         // render the rows now
36110         this.refresh();
36111         if(this.grid.autoSizeColumns){
36112             this.autoSizeColumns();
36113         }
36114     },
36115
36116     beforeInitialResize : function(){
36117
36118     },
36119
36120     onColumnSplitterMoved : function(i, w){
36121         this.userResized = true;
36122         var cm = this.grid.colModel;
36123         cm.setColumnWidth(i, w, true);
36124         var cid = cm.getColumnId(i);
36125         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36126         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36127         this.updateSplitters();
36128         this.layout();
36129         this.grid.fireEvent("columnresize", i, w);
36130     },
36131
36132     syncRowHeights : function(startIndex, endIndex){
36133         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
36134             startIndex = startIndex || 0;
36135             var mrows = this.getBodyTable().rows;
36136             var lrows = this.getLockedTable().rows;
36137             var len = mrows.length-1;
36138             endIndex = Math.min(endIndex || len, len);
36139             for(var i = startIndex; i <= endIndex; i++){
36140                 var m = mrows[i], l = lrows[i];
36141                 var h = Math.max(m.offsetHeight, l.offsetHeight);
36142                 m.style.height = l.style.height = h + "px";
36143             }
36144         }
36145     },
36146
36147     layout : function(initialRender, is2ndPass){
36148         var g = this.grid;
36149         var auto = g.autoHeight;
36150         var scrollOffset = 16;
36151         var c = g.getGridEl(), cm = this.cm,
36152                 expandCol = g.autoExpandColumn,
36153                 gv = this;
36154         //c.beginMeasure();
36155
36156         if(!c.dom.offsetWidth){ // display:none?
36157             if(initialRender){
36158                 this.lockedWrap.show();
36159                 this.mainWrap.show();
36160             }
36161             return;
36162         }
36163
36164         var hasLock = this.cm.isLocked(0);
36165
36166         var tbh = this.headerPanel.getHeight();
36167         var bbh = this.footerPanel.getHeight();
36168
36169         if(auto){
36170             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
36171             var newHeight = ch + c.getBorderWidth("tb");
36172             if(g.maxHeight){
36173                 newHeight = Math.min(g.maxHeight, newHeight);
36174             }
36175             c.setHeight(newHeight);
36176         }
36177
36178         if(g.autoWidth){
36179             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
36180         }
36181
36182         var s = this.scroller;
36183
36184         var csize = c.getSize(true);
36185
36186         this.el.setSize(csize.width, csize.height);
36187
36188         this.headerPanel.setWidth(csize.width);
36189         this.footerPanel.setWidth(csize.width);
36190
36191         var hdHeight = this.mainHd.getHeight();
36192         var vw = csize.width;
36193         var vh = csize.height - (tbh + bbh);
36194
36195         s.setSize(vw, vh);
36196
36197         var bt = this.getBodyTable();
36198         var ltWidth = hasLock ?
36199                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
36200
36201         var scrollHeight = bt.offsetHeight;
36202         var scrollWidth = ltWidth + bt.offsetWidth;
36203         var vscroll = false, hscroll = false;
36204
36205         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
36206
36207         var lw = this.lockedWrap, mw = this.mainWrap;
36208         var lb = this.lockedBody, mb = this.mainBody;
36209
36210         setTimeout(function(){
36211             var t = s.dom.offsetTop;
36212             var w = s.dom.clientWidth,
36213                 h = s.dom.clientHeight;
36214
36215             lw.setTop(t);
36216             lw.setSize(ltWidth, h);
36217
36218             mw.setLeftTop(ltWidth, t);
36219             mw.setSize(w-ltWidth, h);
36220
36221             lb.setHeight(h-hdHeight);
36222             mb.setHeight(h-hdHeight);
36223
36224             if(is2ndPass !== true && !gv.userResized && expandCol){
36225                 // high speed resize without full column calculation
36226                 
36227                 var ci = cm.getIndexById(expandCol);
36228                 if (ci < 0) {
36229                     ci = cm.findColumnIndex(expandCol);
36230                 }
36231                 ci = Math.max(0, ci); // make sure it's got at least the first col.
36232                 var expandId = cm.getColumnId(ci);
36233                 var  tw = cm.getTotalWidth(false);
36234                 var currentWidth = cm.getColumnWidth(ci);
36235                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
36236                 if(currentWidth != cw){
36237                     cm.setColumnWidth(ci, cw, true);
36238                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36239                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36240                     gv.updateSplitters();
36241                     gv.layout(false, true);
36242                 }
36243             }
36244
36245             if(initialRender){
36246                 lw.show();
36247                 mw.show();
36248             }
36249             //c.endMeasure();
36250         }, 10);
36251     },
36252
36253     onWindowResize : function(){
36254         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
36255             return;
36256         }
36257         this.layout();
36258     },
36259
36260     appendFooter : function(parentEl){
36261         return null;
36262     },
36263
36264     sortAscText : "Sort Ascending",
36265     sortDescText : "Sort Descending",
36266     lockText : "Lock Column",
36267     unlockText : "Unlock Column",
36268     columnsText : "Columns"
36269 });
36270
36271
36272 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
36273     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
36274     this.proxy.el.addClass('x-grid3-col-dd');
36275 };
36276
36277 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
36278     handleMouseDown : function(e){
36279
36280     },
36281
36282     callHandleMouseDown : function(e){
36283         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
36284     }
36285 });
36286 /*
36287  * Based on:
36288  * Ext JS Library 1.1.1
36289  * Copyright(c) 2006-2007, Ext JS, LLC.
36290  *
36291  * Originally Released Under LGPL - original licence link has changed is not relivant.
36292  *
36293  * Fork - LGPL
36294  * <script type="text/javascript">
36295  */
36296  
36297 // private
36298 // This is a support class used internally by the Grid components
36299 Roo.grid.SplitDragZone = function(grid, hd, hd2){
36300     this.grid = grid;
36301     this.view = grid.getView();
36302     this.proxy = this.view.resizeProxy;
36303     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
36304         "gridSplitters" + this.grid.getGridEl().id, {
36305         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
36306     });
36307     this.setHandleElId(Roo.id(hd));
36308     this.setOuterHandleElId(Roo.id(hd2));
36309     this.scroll = false;
36310 };
36311 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
36312     fly: Roo.Element.fly,
36313
36314     b4StartDrag : function(x, y){
36315         this.view.headersDisabled = true;
36316         this.proxy.setHeight(this.view.mainWrap.getHeight());
36317         var w = this.cm.getColumnWidth(this.cellIndex);
36318         var minw = Math.max(w-this.grid.minColumnWidth, 0);
36319         this.resetConstraints();
36320         this.setXConstraint(minw, 1000);
36321         this.setYConstraint(0, 0);
36322         this.minX = x - minw;
36323         this.maxX = x + 1000;
36324         this.startPos = x;
36325         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
36326     },
36327
36328
36329     handleMouseDown : function(e){
36330         ev = Roo.EventObject.setEvent(e);
36331         var t = this.fly(ev.getTarget());
36332         if(t.hasClass("x-grid-split")){
36333             this.cellIndex = this.view.getCellIndex(t.dom);
36334             this.split = t.dom;
36335             this.cm = this.grid.colModel;
36336             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
36337                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
36338             }
36339         }
36340     },
36341
36342     endDrag : function(e){
36343         this.view.headersDisabled = false;
36344         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
36345         var diff = endX - this.startPos;
36346         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
36347     },
36348
36349     autoOffset : function(){
36350         this.setDelta(0,0);
36351     }
36352 });/*
36353  * Based on:
36354  * Ext JS Library 1.1.1
36355  * Copyright(c) 2006-2007, Ext JS, LLC.
36356  *
36357  * Originally Released Under LGPL - original licence link has changed is not relivant.
36358  *
36359  * Fork - LGPL
36360  * <script type="text/javascript">
36361  */
36362  
36363 // private
36364 // This is a support class used internally by the Grid components
36365 Roo.grid.GridDragZone = function(grid, config){
36366     this.view = grid.getView();
36367     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
36368     if(this.view.lockedBody){
36369         this.setHandleElId(Roo.id(this.view.mainBody.dom));
36370         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
36371     }
36372     this.scroll = false;
36373     this.grid = grid;
36374     this.ddel = document.createElement('div');
36375     this.ddel.className = 'x-grid-dd-wrap';
36376 };
36377
36378 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
36379     ddGroup : "GridDD",
36380
36381     getDragData : function(e){
36382         var t = Roo.lib.Event.getTarget(e);
36383         var rowIndex = this.view.findRowIndex(t);
36384         if(rowIndex !== false){
36385             var sm = this.grid.selModel;
36386             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
36387               //  sm.mouseDown(e, t);
36388             //}
36389             if (e.hasModifier()){
36390                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
36391             }
36392             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
36393         }
36394         return false;
36395     },
36396
36397     onInitDrag : function(e){
36398         var data = this.dragData;
36399         this.ddel.innerHTML = this.grid.getDragDropText();
36400         this.proxy.update(this.ddel);
36401         // fire start drag?
36402     },
36403
36404     afterRepair : function(){
36405         this.dragging = false;
36406     },
36407
36408     getRepairXY : function(e, data){
36409         return false;
36410     },
36411
36412     onEndDrag : function(data, e){
36413         // fire end drag?
36414     },
36415
36416     onValidDrop : function(dd, e, id){
36417         // fire drag drop?
36418         this.hideProxy();
36419     },
36420
36421     beforeInvalidDrop : function(e, id){
36422
36423     }
36424 });/*
36425  * Based on:
36426  * Ext JS Library 1.1.1
36427  * Copyright(c) 2006-2007, Ext JS, LLC.
36428  *
36429  * Originally Released Under LGPL - original licence link has changed is not relivant.
36430  *
36431  * Fork - LGPL
36432  * <script type="text/javascript">
36433  */
36434  
36435
36436 /**
36437  * @class Roo.grid.ColumnModel
36438  * @extends Roo.util.Observable
36439  * This is the default implementation of a ColumnModel used by the Grid. It defines
36440  * the columns in the grid.
36441  * <br>Usage:<br>
36442  <pre><code>
36443  var colModel = new Roo.grid.ColumnModel([
36444         {header: "Ticker", width: 60, sortable: true, locked: true},
36445         {header: "Company Name", width: 150, sortable: true},
36446         {header: "Market Cap.", width: 100, sortable: true},
36447         {header: "$ Sales", width: 100, sortable: true, renderer: money},
36448         {header: "Employees", width: 100, sortable: true, resizable: false}
36449  ]);
36450  </code></pre>
36451  * <p>
36452  
36453  * The config options listed for this class are options which may appear in each
36454  * individual column definition.
36455  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
36456  * @constructor
36457  * @param {Object} config An Array of column config objects. See this class's
36458  * config objects for details.
36459 */
36460 Roo.grid.ColumnModel = function(config){
36461         /**
36462      * The config passed into the constructor
36463      */
36464     this.config = config;
36465     this.lookup = {};
36466
36467     // if no id, create one
36468     // if the column does not have a dataIndex mapping,
36469     // map it to the order it is in the config
36470     for(var i = 0, len = config.length; i < len; i++){
36471         var c = config[i];
36472         if(typeof c.dataIndex == "undefined"){
36473             c.dataIndex = i;
36474         }
36475         if(typeof c.renderer == "string"){
36476             c.renderer = Roo.util.Format[c.renderer];
36477         }
36478         if(typeof c.id == "undefined"){
36479             c.id = Roo.id();
36480         }
36481         if(c.editor && c.editor.xtype){
36482             c.editor  = Roo.factory(c.editor, Roo.grid);
36483         }
36484         if(c.editor && c.editor.isFormField){
36485             c.editor = new Roo.grid.GridEditor(c.editor);
36486         }
36487         this.lookup[c.id] = c;
36488     }
36489
36490     /**
36491      * The width of columns which have no width specified (defaults to 100)
36492      * @type Number
36493      */
36494     this.defaultWidth = 100;
36495
36496     /**
36497      * Default sortable of columns which have no sortable specified (defaults to false)
36498      * @type Boolean
36499      */
36500     this.defaultSortable = false;
36501
36502     this.addEvents({
36503         /**
36504              * @event widthchange
36505              * Fires when the width of a column changes.
36506              * @param {ColumnModel} this
36507              * @param {Number} columnIndex The column index
36508              * @param {Number} newWidth The new width
36509              */
36510             "widthchange": true,
36511         /**
36512              * @event headerchange
36513              * Fires when the text of a header changes.
36514              * @param {ColumnModel} this
36515              * @param {Number} columnIndex The column index
36516              * @param {Number} newText The new header text
36517              */
36518             "headerchange": true,
36519         /**
36520              * @event hiddenchange
36521              * Fires when a column is hidden or "unhidden".
36522              * @param {ColumnModel} this
36523              * @param {Number} columnIndex The column index
36524              * @param {Boolean} hidden true if hidden, false otherwise
36525              */
36526             "hiddenchange": true,
36527             /**
36528          * @event columnmoved
36529          * Fires when a column is moved.
36530          * @param {ColumnModel} this
36531          * @param {Number} oldIndex
36532          * @param {Number} newIndex
36533          */
36534         "columnmoved" : true,
36535         /**
36536          * @event columlockchange
36537          * Fires when a column's locked state is changed
36538          * @param {ColumnModel} this
36539          * @param {Number} colIndex
36540          * @param {Boolean} locked true if locked
36541          */
36542         "columnlockchange" : true
36543     });
36544     Roo.grid.ColumnModel.superclass.constructor.call(this);
36545 };
36546 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
36547     /**
36548      * @cfg {String} header The header text to display in the Grid view.
36549      */
36550     /**
36551      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
36552      * {@link Roo.data.Record} definition from which to draw the column's value. If not
36553      * specified, the column's index is used as an index into the Record's data Array.
36554      */
36555     /**
36556      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
36557      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
36558      */
36559     /**
36560      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
36561      * Defaults to the value of the {@link #defaultSortable} property.
36562      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
36563      */
36564     /**
36565      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
36566      */
36567     /**
36568      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
36569      */
36570     /**
36571      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
36572      */
36573     /**
36574      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
36575      */
36576     /**
36577      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
36578      * given the cell's data value. See {@link #setRenderer}. If not specified, the
36579      * default renderer uses the raw data value.
36580      */
36581        /**
36582      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
36583      */
36584     /**
36585      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
36586      */
36587
36588     /**
36589      * Returns the id of the column at the specified index.
36590      * @param {Number} index The column index
36591      * @return {String} the id
36592      */
36593     getColumnId : function(index){
36594         return this.config[index].id;
36595     },
36596
36597     /**
36598      * Returns the column for a specified id.
36599      * @param {String} id The column id
36600      * @return {Object} the column
36601      */
36602     getColumnById : function(id){
36603         return this.lookup[id];
36604     },
36605
36606     
36607     /**
36608      * Returns the column for a specified dataIndex.
36609      * @param {String} dataIndex The column dataIndex
36610      * @return {Object|Boolean} the column or false if not found
36611      */
36612     getColumnByDataIndex: function(dataIndex){
36613         var index = this.findColumnIndex(dataIndex);
36614         return index > -1 ? this.config[index] : false;
36615     },
36616     
36617     /**
36618      * Returns the index for a specified column id.
36619      * @param {String} id The column id
36620      * @return {Number} the index, or -1 if not found
36621      */
36622     getIndexById : function(id){
36623         for(var i = 0, len = this.config.length; i < len; i++){
36624             if(this.config[i].id == id){
36625                 return i;
36626             }
36627         }
36628         return -1;
36629     },
36630     
36631     /**
36632      * Returns the index for a specified column dataIndex.
36633      * @param {String} dataIndex The column dataIndex
36634      * @return {Number} the index, or -1 if not found
36635      */
36636     
36637     findColumnIndex : function(dataIndex){
36638         for(var i = 0, len = this.config.length; i < len; i++){
36639             if(this.config[i].dataIndex == dataIndex){
36640                 return i;
36641             }
36642         }
36643         return -1;
36644     },
36645     
36646     
36647     moveColumn : function(oldIndex, newIndex){
36648         var c = this.config[oldIndex];
36649         this.config.splice(oldIndex, 1);
36650         this.config.splice(newIndex, 0, c);
36651         this.dataMap = null;
36652         this.fireEvent("columnmoved", this, oldIndex, newIndex);
36653     },
36654
36655     isLocked : function(colIndex){
36656         return this.config[colIndex].locked === true;
36657     },
36658
36659     setLocked : function(colIndex, value, suppressEvent){
36660         if(this.isLocked(colIndex) == value){
36661             return;
36662         }
36663         this.config[colIndex].locked = value;
36664         if(!suppressEvent){
36665             this.fireEvent("columnlockchange", this, colIndex, value);
36666         }
36667     },
36668
36669     getTotalLockedWidth : function(){
36670         var totalWidth = 0;
36671         for(var i = 0; i < this.config.length; i++){
36672             if(this.isLocked(i) && !this.isHidden(i)){
36673                 this.totalWidth += this.getColumnWidth(i);
36674             }
36675         }
36676         return totalWidth;
36677     },
36678
36679     getLockedCount : function(){
36680         for(var i = 0, len = this.config.length; i < len; i++){
36681             if(!this.isLocked(i)){
36682                 return i;
36683             }
36684         }
36685     },
36686
36687     /**
36688      * Returns the number of columns.
36689      * @return {Number}
36690      */
36691     getColumnCount : function(visibleOnly){
36692         if(visibleOnly === true){
36693             var c = 0;
36694             for(var i = 0, len = this.config.length; i < len; i++){
36695                 if(!this.isHidden(i)){
36696                     c++;
36697                 }
36698             }
36699             return c;
36700         }
36701         return this.config.length;
36702     },
36703
36704     /**
36705      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
36706      * @param {Function} fn
36707      * @param {Object} scope (optional)
36708      * @return {Array} result
36709      */
36710     getColumnsBy : function(fn, scope){
36711         var r = [];
36712         for(var i = 0, len = this.config.length; i < len; i++){
36713             var c = this.config[i];
36714             if(fn.call(scope||this, c, i) === true){
36715                 r[r.length] = c;
36716             }
36717         }
36718         return r;
36719     },
36720
36721     /**
36722      * Returns true if the specified column is sortable.
36723      * @param {Number} col The column index
36724      * @return {Boolean}
36725      */
36726     isSortable : function(col){
36727         if(typeof this.config[col].sortable == "undefined"){
36728             return this.defaultSortable;
36729         }
36730         return this.config[col].sortable;
36731     },
36732
36733     /**
36734      * Returns the rendering (formatting) function defined for the column.
36735      * @param {Number} col The column index.
36736      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
36737      */
36738     getRenderer : function(col){
36739         if(!this.config[col].renderer){
36740             return Roo.grid.ColumnModel.defaultRenderer;
36741         }
36742         return this.config[col].renderer;
36743     },
36744
36745     /**
36746      * Sets the rendering (formatting) function for a column.
36747      * @param {Number} col The column index
36748      * @param {Function} fn The function to use to process the cell's raw data
36749      * to return HTML markup for the grid view. The render function is called with
36750      * the following parameters:<ul>
36751      * <li>Data value.</li>
36752      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
36753      * <li>css A CSS style string to apply to the table cell.</li>
36754      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
36755      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
36756      * <li>Row index</li>
36757      * <li>Column index</li>
36758      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
36759      */
36760     setRenderer : function(col, fn){
36761         this.config[col].renderer = fn;
36762     },
36763
36764     /**
36765      * Returns the width for the specified column.
36766      * @param {Number} col The column index
36767      * @return {Number}
36768      */
36769     getColumnWidth : function(col){
36770         return this.config[col].width * 1 || this.defaultWidth;
36771     },
36772
36773     /**
36774      * Sets the width for a column.
36775      * @param {Number} col The column index
36776      * @param {Number} width The new width
36777      */
36778     setColumnWidth : function(col, width, suppressEvent){
36779         this.config[col].width = width;
36780         this.totalWidth = null;
36781         if(!suppressEvent){
36782              this.fireEvent("widthchange", this, col, width);
36783         }
36784     },
36785
36786     /**
36787      * Returns the total width of all columns.
36788      * @param {Boolean} includeHidden True to include hidden column widths
36789      * @return {Number}
36790      */
36791     getTotalWidth : function(includeHidden){
36792         if(!this.totalWidth){
36793             this.totalWidth = 0;
36794             for(var i = 0, len = this.config.length; i < len; i++){
36795                 if(includeHidden || !this.isHidden(i)){
36796                     this.totalWidth += this.getColumnWidth(i);
36797                 }
36798             }
36799         }
36800         return this.totalWidth;
36801     },
36802
36803     /**
36804      * Returns the header for the specified column.
36805      * @param {Number} col The column index
36806      * @return {String}
36807      */
36808     getColumnHeader : function(col){
36809         return this.config[col].header;
36810     },
36811
36812     /**
36813      * Sets the header for a column.
36814      * @param {Number} col The column index
36815      * @param {String} header The new header
36816      */
36817     setColumnHeader : function(col, header){
36818         this.config[col].header = header;
36819         this.fireEvent("headerchange", this, col, header);
36820     },
36821
36822     /**
36823      * Returns the tooltip for the specified column.
36824      * @param {Number} col The column index
36825      * @return {String}
36826      */
36827     getColumnTooltip : function(col){
36828             return this.config[col].tooltip;
36829     },
36830     /**
36831      * Sets the tooltip for a column.
36832      * @param {Number} col The column index
36833      * @param {String} tooltip The new tooltip
36834      */
36835     setColumnTooltip : function(col, tooltip){
36836             this.config[col].tooltip = tooltip;
36837     },
36838
36839     /**
36840      * Returns the dataIndex for the specified column.
36841      * @param {Number} col The column index
36842      * @return {Number}
36843      */
36844     getDataIndex : function(col){
36845         return this.config[col].dataIndex;
36846     },
36847
36848     /**
36849      * Sets the dataIndex for a column.
36850      * @param {Number} col The column index
36851      * @param {Number} dataIndex The new dataIndex
36852      */
36853     setDataIndex : function(col, dataIndex){
36854         this.config[col].dataIndex = dataIndex;
36855     },
36856
36857     
36858     
36859     /**
36860      * Returns true if the cell is editable.
36861      * @param {Number} colIndex The column index
36862      * @param {Number} rowIndex The row index
36863      * @return {Boolean}
36864      */
36865     isCellEditable : function(colIndex, rowIndex){
36866         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
36867     },
36868
36869     /**
36870      * Returns the editor defined for the cell/column.
36871      * return false or null to disable editing.
36872      * @param {Number} colIndex The column index
36873      * @param {Number} rowIndex The row index
36874      * @return {Object}
36875      */
36876     getCellEditor : function(colIndex, rowIndex){
36877         return this.config[colIndex].editor;
36878     },
36879
36880     /**
36881      * Sets if a column is editable.
36882      * @param {Number} col The column index
36883      * @param {Boolean} editable True if the column is editable
36884      */
36885     setEditable : function(col, editable){
36886         this.config[col].editable = editable;
36887     },
36888
36889
36890     /**
36891      * Returns true if the column is hidden.
36892      * @param {Number} colIndex The column index
36893      * @return {Boolean}
36894      */
36895     isHidden : function(colIndex){
36896         return this.config[colIndex].hidden;
36897     },
36898
36899
36900     /**
36901      * Returns true if the column width cannot be changed
36902      */
36903     isFixed : function(colIndex){
36904         return this.config[colIndex].fixed;
36905     },
36906
36907     /**
36908      * Returns true if the column can be resized
36909      * @return {Boolean}
36910      */
36911     isResizable : function(colIndex){
36912         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
36913     },
36914     /**
36915      * Sets if a column is hidden.
36916      * @param {Number} colIndex The column index
36917      * @param {Boolean} hidden True if the column is hidden
36918      */
36919     setHidden : function(colIndex, hidden){
36920         this.config[colIndex].hidden = hidden;
36921         this.totalWidth = null;
36922         this.fireEvent("hiddenchange", this, colIndex, hidden);
36923     },
36924
36925     /**
36926      * Sets the editor for a column.
36927      * @param {Number} col The column index
36928      * @param {Object} editor The editor object
36929      */
36930     setEditor : function(col, editor){
36931         this.config[col].editor = editor;
36932     }
36933 });
36934
36935 Roo.grid.ColumnModel.defaultRenderer = function(value){
36936         if(typeof value == "string" && value.length < 1){
36937             return "&#160;";
36938         }
36939         return value;
36940 };
36941
36942 // Alias for backwards compatibility
36943 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
36944 /*
36945  * Based on:
36946  * Ext JS Library 1.1.1
36947  * Copyright(c) 2006-2007, Ext JS, LLC.
36948  *
36949  * Originally Released Under LGPL - original licence link has changed is not relivant.
36950  *
36951  * Fork - LGPL
36952  * <script type="text/javascript">
36953  */
36954
36955 /**
36956  * @class Roo.grid.AbstractSelectionModel
36957  * @extends Roo.util.Observable
36958  * Abstract base class for grid SelectionModels.  It provides the interface that should be
36959  * implemented by descendant classes.  This class should not be directly instantiated.
36960  * @constructor
36961  */
36962 Roo.grid.AbstractSelectionModel = function(){
36963     this.locked = false;
36964     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
36965 };
36966
36967 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
36968     /** @ignore Called by the grid automatically. Do not call directly. */
36969     init : function(grid){
36970         this.grid = grid;
36971         this.initEvents();
36972     },
36973
36974     /**
36975      * Locks the selections.
36976      */
36977     lock : function(){
36978         this.locked = true;
36979     },
36980
36981     /**
36982      * Unlocks the selections.
36983      */
36984     unlock : function(){
36985         this.locked = false;
36986     },
36987
36988     /**
36989      * Returns true if the selections are locked.
36990      * @return {Boolean}
36991      */
36992     isLocked : function(){
36993         return this.locked;
36994     }
36995 });/*
36996  * Based on:
36997  * Ext JS Library 1.1.1
36998  * Copyright(c) 2006-2007, Ext JS, LLC.
36999  *
37000  * Originally Released Under LGPL - original licence link has changed is not relivant.
37001  *
37002  * Fork - LGPL
37003  * <script type="text/javascript">
37004  */
37005 /**
37006  * @extends Roo.grid.AbstractSelectionModel
37007  * @class Roo.grid.RowSelectionModel
37008  * The default SelectionModel used by {@link Roo.grid.Grid}.
37009  * It supports multiple selections and keyboard selection/navigation. 
37010  * @constructor
37011  * @param {Object} config
37012  */
37013 Roo.grid.RowSelectionModel = function(config){
37014     Roo.apply(this, config);
37015     this.selections = new Roo.util.MixedCollection(false, function(o){
37016         return o.id;
37017     });
37018
37019     this.last = false;
37020     this.lastActive = false;
37021
37022     this.addEvents({
37023         /**
37024              * @event selectionchange
37025              * Fires when the selection changes
37026              * @param {SelectionModel} this
37027              */
37028             "selectionchange" : true,
37029         /**
37030              * @event afterselectionchange
37031              * Fires after the selection changes (eg. by key press or clicking)
37032              * @param {SelectionModel} this
37033              */
37034             "afterselectionchange" : true,
37035         /**
37036              * @event beforerowselect
37037              * Fires when a row is selected being selected, return false to cancel.
37038              * @param {SelectionModel} this
37039              * @param {Number} rowIndex The selected index
37040              * @param {Boolean} keepExisting False if other selections will be cleared
37041              */
37042             "beforerowselect" : true,
37043         /**
37044              * @event rowselect
37045              * Fires when a row is selected.
37046              * @param {SelectionModel} this
37047              * @param {Number} rowIndex The selected index
37048              * @param {Roo.data.Record} r The record
37049              */
37050             "rowselect" : true,
37051         /**
37052              * @event rowdeselect
37053              * Fires when a row is deselected.
37054              * @param {SelectionModel} this
37055              * @param {Number} rowIndex The selected index
37056              */
37057         "rowdeselect" : true
37058     });
37059     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
37060     this.locked = false;
37061 };
37062
37063 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
37064     /**
37065      * @cfg {Boolean} singleSelect
37066      * True to allow selection of only one row at a time (defaults to false)
37067      */
37068     singleSelect : false,
37069
37070     // private
37071     initEvents : function(){
37072
37073         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
37074             this.grid.on("mousedown", this.handleMouseDown, this);
37075         }else{ // allow click to work like normal
37076             this.grid.on("rowclick", this.handleDragableRowClick, this);
37077         }
37078
37079         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
37080             "up" : function(e){
37081                 if(!e.shiftKey){
37082                     this.selectPrevious(e.shiftKey);
37083                 }else if(this.last !== false && this.lastActive !== false){
37084                     var last = this.last;
37085                     this.selectRange(this.last,  this.lastActive-1);
37086                     this.grid.getView().focusRow(this.lastActive);
37087                     if(last !== false){
37088                         this.last = last;
37089                     }
37090                 }else{
37091                     this.selectFirstRow();
37092                 }
37093                 this.fireEvent("afterselectionchange", this);
37094             },
37095             "down" : function(e){
37096                 if(!e.shiftKey){
37097                     this.selectNext(e.shiftKey);
37098                 }else if(this.last !== false && this.lastActive !== false){
37099                     var last = this.last;
37100                     this.selectRange(this.last,  this.lastActive+1);
37101                     this.grid.getView().focusRow(this.lastActive);
37102                     if(last !== false){
37103                         this.last = last;
37104                     }
37105                 }else{
37106                     this.selectFirstRow();
37107                 }
37108                 this.fireEvent("afterselectionchange", this);
37109             },
37110             scope: this
37111         });
37112
37113         var view = this.grid.view;
37114         view.on("refresh", this.onRefresh, this);
37115         view.on("rowupdated", this.onRowUpdated, this);
37116         view.on("rowremoved", this.onRemove, this);
37117     },
37118
37119     // private
37120     onRefresh : function(){
37121         var ds = this.grid.dataSource, i, v = this.grid.view;
37122         var s = this.selections;
37123         s.each(function(r){
37124             if((i = ds.indexOfId(r.id)) != -1){
37125                 v.onRowSelect(i);
37126             }else{
37127                 s.remove(r);
37128             }
37129         });
37130     },
37131
37132     // private
37133     onRemove : function(v, index, r){
37134         this.selections.remove(r);
37135     },
37136
37137     // private
37138     onRowUpdated : function(v, index, r){
37139         if(this.isSelected(r)){
37140             v.onRowSelect(index);
37141         }
37142     },
37143
37144     /**
37145      * Select records.
37146      * @param {Array} records The records to select
37147      * @param {Boolean} keepExisting (optional) True to keep existing selections
37148      */
37149     selectRecords : function(records, keepExisting){
37150         if(!keepExisting){
37151             this.clearSelections();
37152         }
37153         var ds = this.grid.dataSource;
37154         for(var i = 0, len = records.length; i < len; i++){
37155             this.selectRow(ds.indexOf(records[i]), true);
37156         }
37157     },
37158
37159     /**
37160      * Gets the number of selected rows.
37161      * @return {Number}
37162      */
37163     getCount : function(){
37164         return this.selections.length;
37165     },
37166
37167     /**
37168      * Selects the first row in the grid.
37169      */
37170     selectFirstRow : function(){
37171         this.selectRow(0);
37172     },
37173
37174     /**
37175      * Select the last row.
37176      * @param {Boolean} keepExisting (optional) True to keep existing selections
37177      */
37178     selectLastRow : function(keepExisting){
37179         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
37180     },
37181
37182     /**
37183      * Selects the row immediately following the last selected row.
37184      * @param {Boolean} keepExisting (optional) True to keep existing selections
37185      */
37186     selectNext : function(keepExisting){
37187         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
37188             this.selectRow(this.last+1, keepExisting);
37189             this.grid.getView().focusRow(this.last);
37190         }
37191     },
37192
37193     /**
37194      * Selects the row that precedes the last selected row.
37195      * @param {Boolean} keepExisting (optional) True to keep existing selections
37196      */
37197     selectPrevious : function(keepExisting){
37198         if(this.last){
37199             this.selectRow(this.last-1, keepExisting);
37200             this.grid.getView().focusRow(this.last);
37201         }
37202     },
37203
37204     /**
37205      * Returns the selected records
37206      * @return {Array} Array of selected records
37207      */
37208     getSelections : function(){
37209         return [].concat(this.selections.items);
37210     },
37211
37212     /**
37213      * Returns the first selected record.
37214      * @return {Record}
37215      */
37216     getSelected : function(){
37217         return this.selections.itemAt(0);
37218     },
37219
37220
37221     /**
37222      * Clears all selections.
37223      */
37224     clearSelections : function(fast){
37225         if(this.locked) return;
37226         if(fast !== true){
37227             var ds = this.grid.dataSource;
37228             var s = this.selections;
37229             s.each(function(r){
37230                 this.deselectRow(ds.indexOfId(r.id));
37231             }, this);
37232             s.clear();
37233         }else{
37234             this.selections.clear();
37235         }
37236         this.last = false;
37237     },
37238
37239
37240     /**
37241      * Selects all rows.
37242      */
37243     selectAll : function(){
37244         if(this.locked) return;
37245         this.selections.clear();
37246         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
37247             this.selectRow(i, true);
37248         }
37249     },
37250
37251     /**
37252      * Returns True if there is a selection.
37253      * @return {Boolean}
37254      */
37255     hasSelection : function(){
37256         return this.selections.length > 0;
37257     },
37258
37259     /**
37260      * Returns True if the specified row is selected.
37261      * @param {Number/Record} record The record or index of the record to check
37262      * @return {Boolean}
37263      */
37264     isSelected : function(index){
37265         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
37266         return (r && this.selections.key(r.id) ? true : false);
37267     },
37268
37269     /**
37270      * Returns True if the specified record id is selected.
37271      * @param {String} id The id of record to check
37272      * @return {Boolean}
37273      */
37274     isIdSelected : function(id){
37275         return (this.selections.key(id) ? true : false);
37276     },
37277
37278     // private
37279     handleMouseDown : function(e, t){
37280         var view = this.grid.getView(), rowIndex;
37281         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
37282             return;
37283         };
37284         if(e.shiftKey && this.last !== false){
37285             var last = this.last;
37286             this.selectRange(last, rowIndex, e.ctrlKey);
37287             this.last = last; // reset the last
37288             view.focusRow(rowIndex);
37289         }else{
37290             var isSelected = this.isSelected(rowIndex);
37291             if(e.button !== 0 && isSelected){
37292                 view.focusRow(rowIndex);
37293             }else if(e.ctrlKey && isSelected){
37294                 this.deselectRow(rowIndex);
37295             }else if(!isSelected){
37296                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
37297                 view.focusRow(rowIndex);
37298             }
37299         }
37300         this.fireEvent("afterselectionchange", this);
37301     },
37302     // private
37303     handleDragableRowClick :  function(grid, rowIndex, e) 
37304     {
37305         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
37306             this.selectRow(rowIndex, false);
37307             grid.view.focusRow(rowIndex);
37308              this.fireEvent("afterselectionchange", this);
37309         }
37310     },
37311     
37312     /**
37313      * Selects multiple rows.
37314      * @param {Array} rows Array of the indexes of the row to select
37315      * @param {Boolean} keepExisting (optional) True to keep existing selections
37316      */
37317     selectRows : function(rows, keepExisting){
37318         if(!keepExisting){
37319             this.clearSelections();
37320         }
37321         for(var i = 0, len = rows.length; i < len; i++){
37322             this.selectRow(rows[i], true);
37323         }
37324     },
37325
37326     /**
37327      * Selects a range of rows. All rows in between startRow and endRow are also selected.
37328      * @param {Number} startRow The index of the first row in the range
37329      * @param {Number} endRow The index of the last row in the range
37330      * @param {Boolean} keepExisting (optional) True to retain existing selections
37331      */
37332     selectRange : function(startRow, endRow, keepExisting){
37333         if(this.locked) return;
37334         if(!keepExisting){
37335             this.clearSelections();
37336         }
37337         if(startRow <= endRow){
37338             for(var i = startRow; i <= endRow; i++){
37339                 this.selectRow(i, true);
37340             }
37341         }else{
37342             for(var i = startRow; i >= endRow; i--){
37343                 this.selectRow(i, true);
37344             }
37345         }
37346     },
37347
37348     /**
37349      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
37350      * @param {Number} startRow The index of the first row in the range
37351      * @param {Number} endRow The index of the last row in the range
37352      */
37353     deselectRange : function(startRow, endRow, preventViewNotify){
37354         if(this.locked) return;
37355         for(var i = startRow; i <= endRow; i++){
37356             this.deselectRow(i, preventViewNotify);
37357         }
37358     },
37359
37360     /**
37361      * Selects a row.
37362      * @param {Number} row The index of the row to select
37363      * @param {Boolean} keepExisting (optional) True to keep existing selections
37364      */
37365     selectRow : function(index, keepExisting, preventViewNotify){
37366         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
37367         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
37368             if(!keepExisting || this.singleSelect){
37369                 this.clearSelections();
37370             }
37371             var r = this.grid.dataSource.getAt(index);
37372             this.selections.add(r);
37373             this.last = this.lastActive = index;
37374             if(!preventViewNotify){
37375                 this.grid.getView().onRowSelect(index);
37376             }
37377             this.fireEvent("rowselect", this, index, r);
37378             this.fireEvent("selectionchange", this);
37379         }
37380     },
37381
37382     /**
37383      * Deselects a row.
37384      * @param {Number} row The index of the row to deselect
37385      */
37386     deselectRow : function(index, preventViewNotify){
37387         if(this.locked) return;
37388         if(this.last == index){
37389             this.last = false;
37390         }
37391         if(this.lastActive == index){
37392             this.lastActive = false;
37393         }
37394         var r = this.grid.dataSource.getAt(index);
37395         this.selections.remove(r);
37396         if(!preventViewNotify){
37397             this.grid.getView().onRowDeselect(index);
37398         }
37399         this.fireEvent("rowdeselect", this, index);
37400         this.fireEvent("selectionchange", this);
37401     },
37402
37403     // private
37404     restoreLast : function(){
37405         if(this._last){
37406             this.last = this._last;
37407         }
37408     },
37409
37410     // private
37411     acceptsNav : function(row, col, cm){
37412         return !cm.isHidden(col) && cm.isCellEditable(col, row);
37413     },
37414
37415     // private
37416     onEditorKey : function(field, e){
37417         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
37418         if(k == e.TAB){
37419             e.stopEvent();
37420             ed.completeEdit();
37421             if(e.shiftKey){
37422                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37423             }else{
37424                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37425             }
37426         }else if(k == e.ENTER && !e.ctrlKey){
37427             e.stopEvent();
37428             ed.completeEdit();
37429             if(e.shiftKey){
37430                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
37431             }else{
37432                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
37433             }
37434         }else if(k == e.ESC){
37435             ed.cancelEdit();
37436         }
37437         if(newCell){
37438             g.startEditing(newCell[0], newCell[1]);
37439         }
37440     }
37441 });/*
37442  * Based on:
37443  * Ext JS Library 1.1.1
37444  * Copyright(c) 2006-2007, Ext JS, LLC.
37445  *
37446  * Originally Released Under LGPL - original licence link has changed is not relivant.
37447  *
37448  * Fork - LGPL
37449  * <script type="text/javascript">
37450  */
37451 /**
37452  * @class Roo.grid.CellSelectionModel
37453  * @extends Roo.grid.AbstractSelectionModel
37454  * This class provides the basic implementation for cell selection in a grid.
37455  * @constructor
37456  * @param {Object} config The object containing the configuration of this model.
37457  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
37458  */
37459 Roo.grid.CellSelectionModel = function(config){
37460     Roo.apply(this, config);
37461
37462     this.selection = null;
37463
37464     this.addEvents({
37465         /**
37466              * @event beforerowselect
37467              * Fires before a cell is selected.
37468              * @param {SelectionModel} this
37469              * @param {Number} rowIndex The selected row index
37470              * @param {Number} colIndex The selected cell index
37471              */
37472             "beforecellselect" : true,
37473         /**
37474              * @event cellselect
37475              * Fires when a cell is selected.
37476              * @param {SelectionModel} this
37477              * @param {Number} rowIndex The selected row index
37478              * @param {Number} colIndex The selected cell index
37479              */
37480             "cellselect" : true,
37481         /**
37482              * @event selectionchange
37483              * Fires when the active selection changes.
37484              * @param {SelectionModel} this
37485              * @param {Object} selection null for no selection or an object (o) with two properties
37486                 <ul>
37487                 <li>o.record: the record object for the row the selection is in</li>
37488                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
37489                 </ul>
37490              */
37491             "selectionchange" : true,
37492         /**
37493              * @event tabend
37494              * Fires when the tab (or enter) was pressed on the last editable cell
37495              * You can use this to trigger add new row.
37496              * @param {SelectionModel} this
37497              */
37498             "tabend" : true,
37499          /**
37500              * @event beforeeditnext
37501              * Fires before the next editable sell is made active
37502              * You can use this to skip to another cell or fire the tabend
37503              *    if you set cell to false
37504              * @param {Object} eventdata object : { cell : [ row, col ] } 
37505              */
37506             "beforeeditnext" : true
37507     });
37508     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
37509 };
37510
37511 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
37512     
37513     enter_is_tab: false,
37514
37515     /** @ignore */
37516     initEvents : function(){
37517         this.grid.on("mousedown", this.handleMouseDown, this);
37518         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
37519         var view = this.grid.view;
37520         view.on("refresh", this.onViewChange, this);
37521         view.on("rowupdated", this.onRowUpdated, this);
37522         view.on("beforerowremoved", this.clearSelections, this);
37523         view.on("beforerowsinserted", this.clearSelections, this);
37524         if(this.grid.isEditor){
37525             this.grid.on("beforeedit", this.beforeEdit,  this);
37526         }
37527     },
37528
37529         //private
37530     beforeEdit : function(e){
37531         this.select(e.row, e.column, false, true, e.record);
37532     },
37533
37534         //private
37535     onRowUpdated : function(v, index, r){
37536         if(this.selection && this.selection.record == r){
37537             v.onCellSelect(index, this.selection.cell[1]);
37538         }
37539     },
37540
37541         //private
37542     onViewChange : function(){
37543         this.clearSelections(true);
37544     },
37545
37546         /**
37547          * Returns the currently selected cell,.
37548          * @return {Array} The selected cell (row, column) or null if none selected.
37549          */
37550     getSelectedCell : function(){
37551         return this.selection ? this.selection.cell : null;
37552     },
37553
37554     /**
37555      * Clears all selections.
37556      * @param {Boolean} true to prevent the gridview from being notified about the change.
37557      */
37558     clearSelections : function(preventNotify){
37559         var s = this.selection;
37560         if(s){
37561             if(preventNotify !== true){
37562                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
37563             }
37564             this.selection = null;
37565             this.fireEvent("selectionchange", this, null);
37566         }
37567     },
37568
37569     /**
37570      * Returns true if there is a selection.
37571      * @return {Boolean}
37572      */
37573     hasSelection : function(){
37574         return this.selection ? true : false;
37575     },
37576
37577     /** @ignore */
37578     handleMouseDown : function(e, t){
37579         var v = this.grid.getView();
37580         if(this.isLocked()){
37581             return;
37582         };
37583         var row = v.findRowIndex(t);
37584         var cell = v.findCellIndex(t);
37585         if(row !== false && cell !== false){
37586             this.select(row, cell);
37587         }
37588     },
37589
37590     /**
37591      * Selects a cell.
37592      * @param {Number} rowIndex
37593      * @param {Number} collIndex
37594      */
37595     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
37596         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
37597             this.clearSelections();
37598             r = r || this.grid.dataSource.getAt(rowIndex);
37599             this.selection = {
37600                 record : r,
37601                 cell : [rowIndex, colIndex]
37602             };
37603             if(!preventViewNotify){
37604                 var v = this.grid.getView();
37605                 v.onCellSelect(rowIndex, colIndex);
37606                 if(preventFocus !== true){
37607                     v.focusCell(rowIndex, colIndex);
37608                 }
37609             }
37610             this.fireEvent("cellselect", this, rowIndex, colIndex);
37611             this.fireEvent("selectionchange", this, this.selection);
37612         }
37613     },
37614
37615         //private
37616     isSelectable : function(rowIndex, colIndex, cm){
37617         return !cm.isHidden(colIndex);
37618     },
37619
37620     /** @ignore */
37621     handleKeyDown : function(e){
37622         //Roo.log('Cell Sel Model handleKeyDown');
37623         if(!e.isNavKeyPress()){
37624             return;
37625         }
37626         var g = this.grid, s = this.selection;
37627         if(!s){
37628             e.stopEvent();
37629             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
37630             if(cell){
37631                 this.select(cell[0], cell[1]);
37632             }
37633             return;
37634         }
37635         var sm = this;
37636         var walk = function(row, col, step){
37637             return g.walkCells(row, col, step, sm.isSelectable,  sm);
37638         };
37639         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
37640         var newCell;
37641
37642       
37643
37644         switch(k){
37645             case e.TAB:
37646                 // handled by onEditorKey
37647                 if (g.isEditor && g.editing) {
37648                     return;
37649                 }
37650                 if(e.shiftKey) {
37651                     newCell = walk(r, c-1, -1);
37652                 } else {
37653                     newCell = walk(r, c+1, 1);
37654                 }
37655                 break;
37656             
37657             case e.DOWN:
37658                newCell = walk(r+1, c, 1);
37659                 break;
37660             
37661             case e.UP:
37662                 newCell = walk(r-1, c, -1);
37663                 break;
37664             
37665             case e.RIGHT:
37666                 newCell = walk(r, c+1, 1);
37667                 break;
37668             
37669             case e.LEFT:
37670                 newCell = walk(r, c-1, -1);
37671                 break;
37672             
37673             case e.ENTER:
37674                 
37675                 if(g.isEditor && !g.editing){
37676                    g.startEditing(r, c);
37677                    e.stopEvent();
37678                    return;
37679                 }
37680                 
37681                 
37682              break;
37683         };
37684         if(newCell){
37685             this.select(newCell[0], newCell[1]);
37686             e.stopEvent();
37687             
37688         }
37689     },
37690
37691     acceptsNav : function(row, col, cm){
37692         return !cm.isHidden(col) && cm.isCellEditable(col, row);
37693     },
37694     /**
37695      * Selects a cell.
37696      * @param {Number} field (not used) - as it's normally used as a listener
37697      * @param {Number} e - event - fake it by using
37698      *
37699      * var e = Roo.EventObjectImpl.prototype;
37700      * e.keyCode = e.TAB
37701      *
37702      * 
37703      */
37704     onEditorKey : function(field, e){
37705         
37706         var k = e.getKey(),
37707             newCell,
37708             g = this.grid,
37709             ed = g.activeEditor,
37710             forward = false;
37711         ///Roo.log('onEditorKey' + k);
37712         
37713         
37714         if (this.enter_is_tab && k == e.ENTER) {
37715             k = e.TAB;
37716         }
37717         
37718         if(k == e.TAB){
37719             if(e.shiftKey){
37720                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37721             }else{
37722                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37723                 forward = true;
37724             }
37725             
37726             e.stopEvent();
37727             
37728         } else if(k == e.ENTER &&  !e.ctrlKey){
37729             ed.completeEdit();
37730             e.stopEvent();
37731             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37732         
37733                 } else if(k == e.ESC){
37734             ed.cancelEdit();
37735         }
37736                 
37737         if (newCell) {
37738             var ecall = { cell : newCell, forward : forward };
37739             this.fireEvent('beforeeditnext', ecall );
37740             newCell = ecall.cell;
37741                         forward = ecall.forward;
37742         }
37743                 
37744         if(newCell){
37745             //Roo.log('next cell after edit');
37746             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
37747         } else if (forward) {
37748             // tabbed past last
37749             this.fireEvent.defer(100, this, ['tabend',this]);
37750         }
37751     }
37752 });/*
37753  * Based on:
37754  * Ext JS Library 1.1.1
37755  * Copyright(c) 2006-2007, Ext JS, LLC.
37756  *
37757  * Originally Released Under LGPL - original licence link has changed is not relivant.
37758  *
37759  * Fork - LGPL
37760  * <script type="text/javascript">
37761  */
37762  
37763 /**
37764  * @class Roo.grid.EditorGrid
37765  * @extends Roo.grid.Grid
37766  * Class for creating and editable grid.
37767  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
37768  * The container MUST have some type of size defined for the grid to fill. The container will be 
37769  * automatically set to position relative if it isn't already.
37770  * @param {Object} dataSource The data model to bind to
37771  * @param {Object} colModel The column model with info about this grid's columns
37772  */
37773 Roo.grid.EditorGrid = function(container, config){
37774     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
37775     this.getGridEl().addClass("xedit-grid");
37776
37777     if(!this.selModel){
37778         this.selModel = new Roo.grid.CellSelectionModel();
37779     }
37780
37781     this.activeEditor = null;
37782
37783         this.addEvents({
37784             /**
37785              * @event beforeedit
37786              * Fires before cell editing is triggered. The edit event object has the following properties <br />
37787              * <ul style="padding:5px;padding-left:16px;">
37788              * <li>grid - This grid</li>
37789              * <li>record - The record being edited</li>
37790              * <li>field - The field name being edited</li>
37791              * <li>value - The value for the field being edited.</li>
37792              * <li>row - The grid row index</li>
37793              * <li>column - The grid column index</li>
37794              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37795              * </ul>
37796              * @param {Object} e An edit event (see above for description)
37797              */
37798             "beforeedit" : true,
37799             /**
37800              * @event afteredit
37801              * Fires after a cell is edited. <br />
37802              * <ul style="padding:5px;padding-left:16px;">
37803              * <li>grid - This grid</li>
37804              * <li>record - The record being edited</li>
37805              * <li>field - The field name being edited</li>
37806              * <li>value - The value being set</li>
37807              * <li>originalValue - The original value for the field, before the edit.</li>
37808              * <li>row - The grid row index</li>
37809              * <li>column - The grid column index</li>
37810              * </ul>
37811              * @param {Object} e An edit event (see above for description)
37812              */
37813             "afteredit" : true,
37814             /**
37815              * @event validateedit
37816              * Fires after a cell is edited, but before the value is set in the record. 
37817          * You can use this to modify the value being set in the field, Return false
37818              * to cancel the change. The edit event object has the following properties <br />
37819              * <ul style="padding:5px;padding-left:16px;">
37820          * <li>editor - This editor</li>
37821              * <li>grid - This grid</li>
37822              * <li>record - The record being edited</li>
37823              * <li>field - The field name being edited</li>
37824              * <li>value - The value being set</li>
37825              * <li>originalValue - The original value for the field, before the edit.</li>
37826              * <li>row - The grid row index</li>
37827              * <li>column - The grid column index</li>
37828              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37829              * </ul>
37830              * @param {Object} e An edit event (see above for description)
37831              */
37832             "validateedit" : true
37833         });
37834     this.on("bodyscroll", this.stopEditing,  this);
37835     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
37836 };
37837
37838 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
37839     /**
37840      * @cfg {Number} clicksToEdit
37841      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
37842      */
37843     clicksToEdit: 2,
37844
37845     // private
37846     isEditor : true,
37847     // private
37848     trackMouseOver: false, // causes very odd FF errors
37849
37850     onCellDblClick : function(g, row, col){
37851         this.startEditing(row, col);
37852     },
37853
37854     onEditComplete : function(ed, value, startValue){
37855         this.editing = false;
37856         this.activeEditor = null;
37857         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
37858         var r = ed.record;
37859         var field = this.colModel.getDataIndex(ed.col);
37860         var e = {
37861             grid: this,
37862             record: r,
37863             field: field,
37864             originalValue: startValue,
37865             value: value,
37866             row: ed.row,
37867             column: ed.col,
37868             cancel:false,
37869             editor: ed
37870         };
37871         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
37872         cell.show();
37873           
37874         if(String(value) !== String(startValue)){
37875             
37876             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
37877                 r.set(field, e.value);
37878                 // if we are dealing with a combo box..
37879                 // then we also set the 'name' colum to be the displayField
37880                 if (ed.field.displayField && ed.field.name) {
37881                     r.set(ed.field.name, ed.field.el.dom.value);
37882                 }
37883                 
37884                 delete e.cancel; //?? why!!!
37885                 this.fireEvent("afteredit", e);
37886             }
37887         } else {
37888             this.fireEvent("afteredit", e); // always fire it!
37889         }
37890         this.view.focusCell(ed.row, ed.col);
37891     },
37892
37893     /**
37894      * Starts editing the specified for the specified row/column
37895      * @param {Number} rowIndex
37896      * @param {Number} colIndex
37897      */
37898     startEditing : function(row, col){
37899         this.stopEditing();
37900         if(this.colModel.isCellEditable(col, row)){
37901             this.view.ensureVisible(row, col, true);
37902           
37903             var r = this.dataSource.getAt(row);
37904             var field = this.colModel.getDataIndex(col);
37905             var cell = Roo.get(this.view.getCell(row,col));
37906             var e = {
37907                 grid: this,
37908                 record: r,
37909                 field: field,
37910                 value: r.data[field],
37911                 row: row,
37912                 column: col,
37913                 cancel:false 
37914             };
37915             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
37916                 this.editing = true;
37917                 var ed = this.colModel.getCellEditor(col, row);
37918                 
37919                 if (!ed) {
37920                     return;
37921                 }
37922                 if(!ed.rendered){
37923                     ed.render(ed.parentEl || document.body);
37924                 }
37925                 ed.field.reset();
37926                
37927                 cell.hide();
37928                 
37929                 (function(){ // complex but required for focus issues in safari, ie and opera
37930                     ed.row = row;
37931                     ed.col = col;
37932                     ed.record = r;
37933                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
37934                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
37935                     this.activeEditor = ed;
37936                     var v = r.data[field];
37937                     ed.startEdit(this.view.getCell(row, col), v);
37938                     // combo's with 'displayField and name set
37939                     if (ed.field.displayField && ed.field.name) {
37940                         ed.field.el.dom.value = r.data[ed.field.name];
37941                     }
37942                     
37943                     
37944                 }).defer(50, this);
37945             }
37946         }
37947     },
37948         
37949     /**
37950      * Stops any active editing
37951      */
37952     stopEditing : function(){
37953         if(this.activeEditor){
37954             this.activeEditor.completeEdit();
37955         }
37956         this.activeEditor = null;
37957     }
37958 });/*
37959  * Based on:
37960  * Ext JS Library 1.1.1
37961  * Copyright(c) 2006-2007, Ext JS, LLC.
37962  *
37963  * Originally Released Under LGPL - original licence link has changed is not relivant.
37964  *
37965  * Fork - LGPL
37966  * <script type="text/javascript">
37967  */
37968
37969 // private - not really -- you end up using it !
37970 // This is a support class used internally by the Grid components
37971
37972 /**
37973  * @class Roo.grid.GridEditor
37974  * @extends Roo.Editor
37975  * Class for creating and editable grid elements.
37976  * @param {Object} config any settings (must include field)
37977  */
37978 Roo.grid.GridEditor = function(field, config){
37979     if (!config && field.field) {
37980         config = field;
37981         field = Roo.factory(config.field, Roo.form);
37982     }
37983     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
37984     field.monitorTab = false;
37985 };
37986
37987 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
37988     
37989     /**
37990      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
37991      */
37992     
37993     alignment: "tl-tl",
37994     autoSize: "width",
37995     hideEl : false,
37996     cls: "x-small-editor x-grid-editor",
37997     shim:false,
37998     shadow:"frame"
37999 });/*
38000  * Based on:
38001  * Ext JS Library 1.1.1
38002  * Copyright(c) 2006-2007, Ext JS, LLC.
38003  *
38004  * Originally Released Under LGPL - original licence link has changed is not relivant.
38005  *
38006  * Fork - LGPL
38007  * <script type="text/javascript">
38008  */
38009   
38010
38011   
38012 Roo.grid.PropertyRecord = Roo.data.Record.create([
38013     {name:'name',type:'string'},  'value'
38014 ]);
38015
38016
38017 Roo.grid.PropertyStore = function(grid, source){
38018     this.grid = grid;
38019     this.store = new Roo.data.Store({
38020         recordType : Roo.grid.PropertyRecord
38021     });
38022     this.store.on('update', this.onUpdate,  this);
38023     if(source){
38024         this.setSource(source);
38025     }
38026     Roo.grid.PropertyStore.superclass.constructor.call(this);
38027 };
38028
38029
38030
38031 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
38032     setSource : function(o){
38033         this.source = o;
38034         this.store.removeAll();
38035         var data = [];
38036         for(var k in o){
38037             if(this.isEditableValue(o[k])){
38038                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
38039             }
38040         }
38041         this.store.loadRecords({records: data}, {}, true);
38042     },
38043
38044     onUpdate : function(ds, record, type){
38045         if(type == Roo.data.Record.EDIT){
38046             var v = record.data['value'];
38047             var oldValue = record.modified['value'];
38048             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
38049                 this.source[record.id] = v;
38050                 record.commit();
38051                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
38052             }else{
38053                 record.reject();
38054             }
38055         }
38056     },
38057
38058     getProperty : function(row){
38059        return this.store.getAt(row);
38060     },
38061
38062     isEditableValue: function(val){
38063         if(val && val instanceof Date){
38064             return true;
38065         }else if(typeof val == 'object' || typeof val == 'function'){
38066             return false;
38067         }
38068         return true;
38069     },
38070
38071     setValue : function(prop, value){
38072         this.source[prop] = value;
38073         this.store.getById(prop).set('value', value);
38074     },
38075
38076     getSource : function(){
38077         return this.source;
38078     }
38079 });
38080
38081 Roo.grid.PropertyColumnModel = function(grid, store){
38082     this.grid = grid;
38083     var g = Roo.grid;
38084     g.PropertyColumnModel.superclass.constructor.call(this, [
38085         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
38086         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
38087     ]);
38088     this.store = store;
38089     this.bselect = Roo.DomHelper.append(document.body, {
38090         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
38091             {tag: 'option', value: 'true', html: 'true'},
38092             {tag: 'option', value: 'false', html: 'false'}
38093         ]
38094     });
38095     Roo.id(this.bselect);
38096     var f = Roo.form;
38097     this.editors = {
38098         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
38099         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
38100         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
38101         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
38102         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
38103     };
38104     this.renderCellDelegate = this.renderCell.createDelegate(this);
38105     this.renderPropDelegate = this.renderProp.createDelegate(this);
38106 };
38107
38108 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
38109     
38110     
38111     nameText : 'Name',
38112     valueText : 'Value',
38113     
38114     dateFormat : 'm/j/Y',
38115     
38116     
38117     renderDate : function(dateVal){
38118         return dateVal.dateFormat(this.dateFormat);
38119     },
38120
38121     renderBool : function(bVal){
38122         return bVal ? 'true' : 'false';
38123     },
38124
38125     isCellEditable : function(colIndex, rowIndex){
38126         return colIndex == 1;
38127     },
38128
38129     getRenderer : function(col){
38130         return col == 1 ?
38131             this.renderCellDelegate : this.renderPropDelegate;
38132     },
38133
38134     renderProp : function(v){
38135         return this.getPropertyName(v);
38136     },
38137
38138     renderCell : function(val){
38139         var rv = val;
38140         if(val instanceof Date){
38141             rv = this.renderDate(val);
38142         }else if(typeof val == 'boolean'){
38143             rv = this.renderBool(val);
38144         }
38145         return Roo.util.Format.htmlEncode(rv);
38146     },
38147
38148     getPropertyName : function(name){
38149         var pn = this.grid.propertyNames;
38150         return pn && pn[name] ? pn[name] : name;
38151     },
38152
38153     getCellEditor : function(colIndex, rowIndex){
38154         var p = this.store.getProperty(rowIndex);
38155         var n = p.data['name'], val = p.data['value'];
38156         
38157         if(typeof(this.grid.customEditors[n]) == 'string'){
38158             return this.editors[this.grid.customEditors[n]];
38159         }
38160         if(typeof(this.grid.customEditors[n]) != 'undefined'){
38161             return this.grid.customEditors[n];
38162         }
38163         if(val instanceof Date){
38164             return this.editors['date'];
38165         }else if(typeof val == 'number'){
38166             return this.editors['number'];
38167         }else if(typeof val == 'boolean'){
38168             return this.editors['boolean'];
38169         }else{
38170             return this.editors['string'];
38171         }
38172     }
38173 });
38174
38175 /**
38176  * @class Roo.grid.PropertyGrid
38177  * @extends Roo.grid.EditorGrid
38178  * This class represents the  interface of a component based property grid control.
38179  * <br><br>Usage:<pre><code>
38180  var grid = new Roo.grid.PropertyGrid("my-container-id", {
38181       
38182  });
38183  // set any options
38184  grid.render();
38185  * </code></pre>
38186   
38187  * @constructor
38188  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
38189  * The container MUST have some type of size defined for the grid to fill. The container will be
38190  * automatically set to position relative if it isn't already.
38191  * @param {Object} config A config object that sets properties on this grid.
38192  */
38193 Roo.grid.PropertyGrid = function(container, config){
38194     config = config || {};
38195     var store = new Roo.grid.PropertyStore(this);
38196     this.store = store;
38197     var cm = new Roo.grid.PropertyColumnModel(this, store);
38198     store.store.sort('name', 'ASC');
38199     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
38200         ds: store.store,
38201         cm: cm,
38202         enableColLock:false,
38203         enableColumnMove:false,
38204         stripeRows:false,
38205         trackMouseOver: false,
38206         clicksToEdit:1
38207     }, config));
38208     this.getGridEl().addClass('x-props-grid');
38209     this.lastEditRow = null;
38210     this.on('columnresize', this.onColumnResize, this);
38211     this.addEvents({
38212          /**
38213              * @event beforepropertychange
38214              * Fires before a property changes (return false to stop?)
38215              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38216              * @param {String} id Record Id
38217              * @param {String} newval New Value
38218          * @param {String} oldval Old Value
38219              */
38220         "beforepropertychange": true,
38221         /**
38222              * @event propertychange
38223              * Fires after a property changes
38224              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38225              * @param {String} id Record Id
38226              * @param {String} newval New Value
38227          * @param {String} oldval Old Value
38228              */
38229         "propertychange": true
38230     });
38231     this.customEditors = this.customEditors || {};
38232 };
38233 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
38234     
38235      /**
38236      * @cfg {Object} customEditors map of colnames=> custom editors.
38237      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
38238      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
38239      * false disables editing of the field.
38240          */
38241     
38242       /**
38243      * @cfg {Object} propertyNames map of property Names to their displayed value
38244          */
38245     
38246     render : function(){
38247         Roo.grid.PropertyGrid.superclass.render.call(this);
38248         this.autoSize.defer(100, this);
38249     },
38250
38251     autoSize : function(){
38252         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
38253         if(this.view){
38254             this.view.fitColumns();
38255         }
38256     },
38257
38258     onColumnResize : function(){
38259         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
38260         this.autoSize();
38261     },
38262     /**
38263      * Sets the data for the Grid
38264      * accepts a Key => Value object of all the elements avaiable.
38265      * @param {Object} data  to appear in grid.
38266      */
38267     setSource : function(source){
38268         this.store.setSource(source);
38269         //this.autoSize();
38270     },
38271     /**
38272      * Gets all the data from the grid.
38273      * @return {Object} data  data stored in grid
38274      */
38275     getSource : function(){
38276         return this.store.getSource();
38277     }
38278 });/*
38279  * Based on:
38280  * Ext JS Library 1.1.1
38281  * Copyright(c) 2006-2007, Ext JS, LLC.
38282  *
38283  * Originally Released Under LGPL - original licence link has changed is not relivant.
38284  *
38285  * Fork - LGPL
38286  * <script type="text/javascript">
38287  */
38288  
38289 /**
38290  * @class Roo.LoadMask
38291  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38292  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38293  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38294  * element's UpdateManager load indicator and will be destroyed after the initial load.
38295  * @constructor
38296  * Create a new LoadMask
38297  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38298  * @param {Object} config The config object
38299  */
38300 Roo.LoadMask = function(el, config){
38301     this.el = Roo.get(el);
38302     Roo.apply(this, config);
38303     if(this.store){
38304         this.store.on('beforeload', this.onBeforeLoad, this);
38305         this.store.on('load', this.onLoad, this);
38306         this.store.on('loadexception', this.onLoadException, this);
38307         this.removeMask = false;
38308     }else{
38309         var um = this.el.getUpdateManager();
38310         um.showLoadIndicator = false; // disable the default indicator
38311         um.on('beforeupdate', this.onBeforeLoad, this);
38312         um.on('update', this.onLoad, this);
38313         um.on('failure', this.onLoad, this);
38314         this.removeMask = true;
38315     }
38316 };
38317
38318 Roo.LoadMask.prototype = {
38319     /**
38320      * @cfg {Boolean} removeMask
38321      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38322      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38323      */
38324     /**
38325      * @cfg {String} msg
38326      * The text to display in a centered loading message box (defaults to 'Loading...')
38327      */
38328     msg : 'Loading...',
38329     /**
38330      * @cfg {String} msgCls
38331      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38332      */
38333     msgCls : 'x-mask-loading',
38334
38335     /**
38336      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38337      * @type Boolean
38338      */
38339     disabled: false,
38340
38341     /**
38342      * Disables the mask to prevent it from being displayed
38343      */
38344     disable : function(){
38345        this.disabled = true;
38346     },
38347
38348     /**
38349      * Enables the mask so that it can be displayed
38350      */
38351     enable : function(){
38352         this.disabled = false;
38353     },
38354     
38355     onLoadException : function()
38356     {
38357         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38358             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38359         }
38360         this.el.unmask(this.removeMask);
38361     },
38362     // private
38363     onLoad : function()
38364     {
38365         this.el.unmask(this.removeMask);
38366     },
38367
38368     // private
38369     onBeforeLoad : function(){
38370         if(!this.disabled){
38371             this.el.mask(this.msg, this.msgCls);
38372         }
38373     },
38374
38375     // private
38376     destroy : function(){
38377         if(this.store){
38378             this.store.un('beforeload', this.onBeforeLoad, this);
38379             this.store.un('load', this.onLoad, this);
38380             this.store.un('loadexception', this.onLoadException, this);
38381         }else{
38382             var um = this.el.getUpdateManager();
38383             um.un('beforeupdate', this.onBeforeLoad, this);
38384             um.un('update', this.onLoad, this);
38385             um.un('failure', this.onLoad, this);
38386         }
38387     }
38388 };/*
38389  * Based on:
38390  * Ext JS Library 1.1.1
38391  * Copyright(c) 2006-2007, Ext JS, LLC.
38392  *
38393  * Originally Released Under LGPL - original licence link has changed is not relivant.
38394  *
38395  * Fork - LGPL
38396  * <script type="text/javascript">
38397  */
38398
38399
38400 /**
38401  * @class Roo.XTemplate
38402  * @extends Roo.Template
38403  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38404 <pre><code>
38405 var t = new Roo.XTemplate(
38406         '&lt;select name="{name}"&gt;',
38407                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38408         '&lt;/select&gt;'
38409 );
38410  
38411 // then append, applying the master template values
38412  </code></pre>
38413  *
38414  * Supported features:
38415  *
38416  *  Tags:
38417
38418 <pre><code>
38419       {a_variable} - output encoded.
38420       {a_variable.format:("Y-m-d")} - call a method on the variable
38421       {a_variable:raw} - unencoded output
38422       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38423       {a_variable:this.method_on_template(...)} - call a method on the template object.
38424  
38425 </code></pre>
38426  *  The tpl tag:
38427 <pre><code>
38428         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38429         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38430         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38431         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38432   
38433         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38434         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38435 </code></pre>
38436  *      
38437  */
38438 Roo.XTemplate = function()
38439 {
38440     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38441     if (this.html) {
38442         this.compile();
38443     }
38444 };
38445
38446
38447 Roo.extend(Roo.XTemplate, Roo.Template, {
38448
38449     /**
38450      * The various sub templates
38451      */
38452     tpls : false,
38453     /**
38454      *
38455      * basic tag replacing syntax
38456      * WORD:WORD()
38457      *
38458      * // you can fake an object call by doing this
38459      *  x.t:(test,tesT) 
38460      * 
38461      */
38462     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38463
38464     /**
38465      * compile the template
38466      *
38467      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38468      *
38469      */
38470     compile: function()
38471     {
38472         var s = this.html;
38473      
38474         s = ['<tpl>', s, '</tpl>'].join('');
38475     
38476         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38477             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38478             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38479             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38480             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38481             m,
38482             id     = 0,
38483             tpls   = [];
38484     
38485         while(true == !!(m = s.match(re))){
38486             var forMatch   = m[0].match(nameRe),
38487                 ifMatch   = m[0].match(ifRe),
38488                 execMatch   = m[0].match(execRe),
38489                 namedMatch   = m[0].match(namedRe),
38490                 
38491                 exp  = null, 
38492                 fn   = null,
38493                 exec = null,
38494                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38495                 
38496             if (ifMatch) {
38497                 // if - puts fn into test..
38498                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38499                 if(exp){
38500                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38501                 }
38502             }
38503             
38504             if (execMatch) {
38505                 // exec - calls a function... returns empty if true is  returned.
38506                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38507                 if(exp){
38508                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38509                 }
38510             }
38511             
38512             
38513             if (name) {
38514                 // for = 
38515                 switch(name){
38516                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38517                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38518                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38519                 }
38520             }
38521             var uid = namedMatch ? namedMatch[1] : id;
38522             
38523             
38524             tpls.push({
38525                 id:     namedMatch ? namedMatch[1] : id,
38526                 target: name,
38527                 exec:   exec,
38528                 test:   fn,
38529                 body:   m[1] || ''
38530             });
38531             if (namedMatch) {
38532                 s = s.replace(m[0], '');
38533             } else { 
38534                 s = s.replace(m[0], '{xtpl'+ id + '}');
38535             }
38536             ++id;
38537         }
38538         this.tpls = [];
38539         for(var i = tpls.length-1; i >= 0; --i){
38540             this.compileTpl(tpls[i]);
38541             this.tpls[tpls[i].id] = tpls[i];
38542         }
38543         this.master = tpls[tpls.length-1];
38544         return this;
38545     },
38546     /**
38547      * same as applyTemplate, except it's done to one of the subTemplates
38548      * when using named templates, you can do:
38549      *
38550      * var str = pl.applySubTemplate('your-name', values);
38551      *
38552      * 
38553      * @param {Number} id of the template
38554      * @param {Object} values to apply to template
38555      * @param {Object} parent (normaly the instance of this object)
38556      */
38557     applySubTemplate : function(id, values, parent)
38558     {
38559         
38560         
38561         var t = this.tpls[id];
38562         
38563         
38564         try { 
38565             if(t.test && !t.test.call(this, values, parent)){
38566                 return '';
38567             }
38568         } catch(e) {
38569             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38570             Roo.log(e.toString());
38571             Roo.log(t.test);
38572             return ''
38573         }
38574         try { 
38575             
38576             if(t.exec && t.exec.call(this, values, parent)){
38577                 return '';
38578             }
38579         } catch(e) {
38580             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38581             Roo.log(e.toString());
38582             Roo.log(t.exec);
38583             return ''
38584         }
38585         try {
38586             var vs = t.target ? t.target.call(this, values, parent) : values;
38587             parent = t.target ? values : parent;
38588             if(t.target && vs instanceof Array){
38589                 var buf = [];
38590                 for(var i = 0, len = vs.length; i < len; i++){
38591                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38592                 }
38593                 return buf.join('');
38594             }
38595             return t.compiled.call(this, vs, parent);
38596         } catch (e) {
38597             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38598             Roo.log(e.toString());
38599             Roo.log(t.compiled);
38600             return '';
38601         }
38602     },
38603
38604     compileTpl : function(tpl)
38605     {
38606         var fm = Roo.util.Format;
38607         var useF = this.disableFormats !== true;
38608         var sep = Roo.isGecko ? "+" : ",";
38609         var undef = function(str) {
38610             Roo.log("Property not found :"  + str);
38611             return '';
38612         };
38613         
38614         var fn = function(m, name, format, args)
38615         {
38616             //Roo.log(arguments);
38617             args = args ? args.replace(/\\'/g,"'") : args;
38618             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38619             if (typeof(format) == 'undefined') {
38620                 format= 'htmlEncode';
38621             }
38622             if (format == 'raw' ) {
38623                 format = false;
38624             }
38625             
38626             if(name.substr(0, 4) == 'xtpl'){
38627                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38628             }
38629             
38630             // build an array of options to determine if value is undefined..
38631             
38632             // basically get 'xxxx.yyyy' then do
38633             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38634             //    (function () { Roo.log("Property not found"); return ''; })() :
38635             //    ......
38636             
38637             var udef_ar = [];
38638             var lookfor = '';
38639             Roo.each(name.split('.'), function(st) {
38640                 lookfor += (lookfor.length ? '.': '') + st;
38641                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38642             });
38643             
38644             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38645             
38646             
38647             if(format && useF){
38648                 
38649                 args = args ? ',' + args : "";
38650                  
38651                 if(format.substr(0, 5) != "this."){
38652                     format = "fm." + format + '(';
38653                 }else{
38654                     format = 'this.call("'+ format.substr(5) + '", ';
38655                     args = ", values";
38656                 }
38657                 
38658                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38659             }
38660              
38661             if (args.length) {
38662                 // called with xxyx.yuu:(test,test)
38663                 // change to ()
38664                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38665             }
38666             // raw.. - :raw modifier..
38667             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38668             
38669         };
38670         var body;
38671         // branched to use + in gecko and [].join() in others
38672         if(Roo.isGecko){
38673             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38674                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38675                     "';};};";
38676         }else{
38677             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38678             body.push(tpl.body.replace(/(\r\n|\n)/g,
38679                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38680             body.push("'].join('');};};");
38681             body = body.join('');
38682         }
38683         
38684         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38685        
38686         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38687         eval(body);
38688         
38689         return this;
38690     },
38691
38692     applyTemplate : function(values){
38693         return this.master.compiled.call(this, values, {});
38694         //var s = this.subs;
38695     },
38696
38697     apply : function(){
38698         return this.applyTemplate.apply(this, arguments);
38699     }
38700
38701  });
38702
38703 Roo.XTemplate.from = function(el){
38704     el = Roo.getDom(el);
38705     return new Roo.XTemplate(el.value || el.innerHTML);
38706 };/*
38707  * Original code for Roojs - LGPL
38708  * <script type="text/javascript">
38709  */
38710  
38711 /**
38712  * @class Roo.XComponent
38713  * A delayed Element creator...
38714  * Or a way to group chunks of interface together.
38715  * 
38716  * Mypart.xyx = new Roo.XComponent({
38717
38718     parent : 'Mypart.xyz', // empty == document.element.!!
38719     order : '001',
38720     name : 'xxxx'
38721     region : 'xxxx'
38722     disabled : function() {} 
38723      
38724     tree : function() { // return an tree of xtype declared components
38725         var MODULE = this;
38726         return 
38727         {
38728             xtype : 'NestedLayoutPanel',
38729             // technicall
38730         }
38731      ]
38732  *})
38733  *
38734  *
38735  * It can be used to build a big heiracy, with parent etc.
38736  * or you can just use this to render a single compoent to a dom element
38737  * MYPART.render(Roo.Element | String(id) | dom_element )
38738  * 
38739  * @extends Roo.util.Observable
38740  * @constructor
38741  * @param cfg {Object} configuration of component
38742  * 
38743  */
38744 Roo.XComponent = function(cfg) {
38745     Roo.apply(this, cfg);
38746     this.addEvents({ 
38747         /**
38748              * @event built
38749              * Fires when this the componnt is built
38750              * @param {Roo.XComponent} c the component
38751              */
38752         'built' : true
38753         
38754     });
38755     this.region = this.region || 'center'; // default..
38756     Roo.XComponent.register(this);
38757     this.modules = false;
38758     this.el = false; // where the layout goes..
38759     
38760     
38761 }
38762 Roo.extend(Roo.XComponent, Roo.util.Observable, {
38763     /**
38764      * @property el
38765      * The created element (with Roo.factory())
38766      * @type {Roo.Layout}
38767      */
38768     el  : false,
38769     
38770     /**
38771      * @property el
38772      * for BC  - use el in new code
38773      * @type {Roo.Layout}
38774      */
38775     panel : false,
38776     
38777     /**
38778      * @property layout
38779      * for BC  - use el in new code
38780      * @type {Roo.Layout}
38781      */
38782     layout : false,
38783     
38784      /**
38785      * @cfg {Function|boolean} disabled
38786      * If this module is disabled by some rule, return true from the funtion
38787      */
38788     disabled : false,
38789     
38790     /**
38791      * @cfg {String} parent 
38792      * Name of parent element which it get xtype added to..
38793      */
38794     parent: false,
38795     
38796     /**
38797      * @cfg {String} order
38798      * Used to set the order in which elements are created (usefull for multiple tabs)
38799      */
38800     
38801     order : false,
38802     /**
38803      * @cfg {String} name
38804      * String to display while loading.
38805      */
38806     name : false,
38807     /**
38808      * @cfg {String} region
38809      * Region to render component to (defaults to center)
38810      */
38811     region : 'center',
38812     
38813     /**
38814      * @cfg {Array} items
38815      * A single item array - the first element is the root of the tree..
38816      * It's done this way to stay compatible with the Xtype system...
38817      */
38818     items : false,
38819     
38820     /**
38821      * @property _tree
38822      * The method that retuns the tree of parts that make up this compoennt 
38823      * @type {function}
38824      */
38825     _tree  : false,
38826     
38827      /**
38828      * render
38829      * render element to dom or tree
38830      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
38831      */
38832     
38833     render : function(el)
38834     {
38835         
38836         el = el || false;
38837         var hp = this.parent ? 1 : 0;
38838         
38839         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
38840             // if parent is a '#.....' string, then let's use that..
38841             var ename = this.parent.substr(1)
38842             this.parent = false;
38843             el = Roo.get(ename);
38844             if (!el) {
38845                 Roo.log("Warning - element can not be found :#" + ename );
38846                 return;
38847             }
38848         }
38849         
38850         
38851         if (!this.parent) {
38852             
38853             el = el ? Roo.get(el) : false;      
38854             
38855             // it's a top level one..
38856             this.parent =  {
38857                 el : new Roo.BorderLayout(el || document.body, {
38858                 
38859                      center: {
38860                          titlebar: false,
38861                          autoScroll:false,
38862                          closeOnTab: true,
38863                          tabPosition: 'top',
38864                           //resizeTabs: true,
38865                          alwaysShowTabs: el && hp? false :  true,
38866                          hideTabs: el || !hp ? true :  false,
38867                          minTabWidth: 140
38868                      }
38869                  })
38870             }
38871         }
38872         
38873                 if (!this.parent.el) {
38874                         // probably an old style ctor, which has been disabled.
38875                         return;
38876                         
38877                 }
38878                 // The 'tree' method is  '_tree now' 
38879             
38880         var tree = this._tree ? this._tree() : this.tree();
38881         tree.region = tree.region || this.region;
38882         this.el = this.parent.el.addxtype(tree);
38883         this.fireEvent('built', this);
38884         
38885         this.panel = this.el;
38886         this.layout = this.panel.layout;
38887                 this.parentLayout = this.parent.layout  || false;  
38888          
38889     }
38890     
38891 });
38892
38893 Roo.apply(Roo.XComponent, {
38894     /**
38895      * @property  hideProgress
38896      * true to disable the building progress bar.. usefull on single page renders.
38897      * @type Boolean
38898      */
38899     hideProgress : false,
38900     /**
38901      * @property  buildCompleted
38902      * True when the builder has completed building the interface.
38903      * @type Boolean
38904      */
38905     buildCompleted : false,
38906      
38907     /**
38908      * @property  topModule
38909      * the upper most module - uses document.element as it's constructor.
38910      * @type Object
38911      */
38912      
38913     topModule  : false,
38914       
38915     /**
38916      * @property  modules
38917      * array of modules to be created by registration system.
38918      * @type {Array} of Roo.XComponent
38919      */
38920     
38921     modules : [],
38922     /**
38923      * @property  elmodules
38924      * array of modules to be created by which use #ID 
38925      * @type {Array} of Roo.XComponent
38926      */
38927      
38928     elmodules : [],
38929
38930     
38931     /**
38932      * Register components to be built later.
38933      *
38934      * This solves the following issues
38935      * - Building is not done on page load, but after an authentication process has occured.
38936      * - Interface elements are registered on page load
38937      * - Parent Interface elements may not be loaded before child, so this handles that..
38938      * 
38939      *
38940      * example:
38941      * 
38942      * MyApp.register({
38943           order : '000001',
38944           module : 'Pman.Tab.projectMgr',
38945           region : 'center',
38946           parent : 'Pman.layout',
38947           disabled : false,  // or use a function..
38948         })
38949      
38950      * * @param {Object} details about module
38951      */
38952     register : function(obj) {
38953                 
38954         Roo.XComponent.event.fireEvent('register', obj);
38955         switch(typeof(obj.disabled) ) {
38956                 
38957             case 'undefined':
38958                 break;
38959             
38960             case 'function':
38961                 if ( obj.disabled() ) {
38962                         return;
38963                 }
38964                 break;
38965             
38966             default:
38967                 if (obj.disabled) {
38968                         return;
38969                 }
38970                 break;
38971         }
38972                 
38973         this.modules.push(obj);
38974          
38975     },
38976     /**
38977      * convert a string to an object..
38978      * eg. 'AAA.BBB' -> finds AAA.BBB
38979
38980      */
38981     
38982     toObject : function(str)
38983     {
38984         if (!str || typeof(str) == 'object') {
38985             return str;
38986         }
38987         if (str.substring(0,1) == '#') {
38988             return str;
38989         }
38990
38991         var ar = str.split('.');
38992         var rt, o;
38993         rt = ar.shift();
38994             /** eval:var:o */
38995         try {
38996             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
38997         } catch (e) {
38998             throw "Module not found : " + str;
38999         }
39000         
39001         if (o === false) {
39002             throw "Module not found : " + str;
39003         }
39004         Roo.each(ar, function(e) {
39005             if (typeof(o[e]) == 'undefined') {
39006                 throw "Module not found : " + str;
39007             }
39008             o = o[e];
39009         });
39010         
39011         return o;
39012         
39013     },
39014     
39015     
39016     /**
39017      * move modules into their correct place in the tree..
39018      * 
39019      */
39020     preBuild : function ()
39021     {
39022         var _t = this;
39023         Roo.each(this.modules , function (obj)
39024         {
39025             Roo.XComponent.event.fireEvent('beforebuild', obj);
39026             
39027             var opar = obj.parent;
39028             try { 
39029                 obj.parent = this.toObject(opar);
39030             } catch(e) {
39031                 Roo.log("parent:toObject failed: " + e.toString());
39032                 return;
39033             }
39034             
39035             if (!obj.parent) {
39036                 Roo.debug && Roo.log("GOT top level module");
39037                 Roo.debug && Roo.log(obj);
39038                 obj.modules = new Roo.util.MixedCollection(false, 
39039                     function(o) { return o.order + '' }
39040                 );
39041                 this.topModule = obj;
39042                 return;
39043             }
39044                         // parent is a string (usually a dom element name..)
39045             if (typeof(obj.parent) == 'string') {
39046                 this.elmodules.push(obj);
39047                 return;
39048             }
39049             if (obj.parent.constructor != Roo.XComponent) {
39050                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
39051             }
39052             if (!obj.parent.modules) {
39053                 obj.parent.modules = new Roo.util.MixedCollection(false, 
39054                     function(o) { return o.order + '' }
39055                 );
39056             }
39057             if (obj.parent.disabled) {
39058                 obj.disabled = true;
39059             }
39060             obj.parent.modules.add(obj);
39061         }, this);
39062     },
39063     
39064      /**
39065      * make a list of modules to build.
39066      * @return {Array} list of modules. 
39067      */ 
39068     
39069     buildOrder : function()
39070     {
39071         var _this = this;
39072         var cmp = function(a,b) {   
39073             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
39074         };
39075         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
39076             throw "No top level modules to build";
39077         }
39078         
39079         // make a flat list in order of modules to build.
39080         var mods = this.topModule ? [ this.topModule ] : [];
39081                 
39082         // elmodules (is a list of DOM based modules )
39083         Roo.each(this.elmodules, function(e) {
39084             mods.push(e)
39085         });
39086
39087         
39088         // add modules to their parents..
39089         var addMod = function(m) {
39090             Roo.debug && Roo.log("build Order: add: " + m.name);
39091             
39092         mods.push(m);
39093         if (m.modules && !m.disabled) {
39094             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
39095             m.modules.keySort('ASC',  cmp );
39096             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
39097
39098             m.modules.each(addMod);
39099         } else {
39100             Roo.debug && Roo.log("build Order: no child modules");
39101             }
39102             // not sure if this is used any more..
39103             if (m.finalize) {
39104                 m.finalize.name = m.name + " (clean up) ";
39105                 mods.push(m.finalize);
39106             }
39107             
39108         }
39109         if (this.topModule) { 
39110             this.topModule.modules.keySort('ASC',  cmp );
39111             this.topModule.modules.each(addMod);
39112         }
39113         return mods;
39114     },
39115     
39116      /**
39117      * Build the registered modules.
39118      * @param {Object} parent element.
39119      * @param {Function} optional method to call after module has been added.
39120      * 
39121      */ 
39122    
39123     build : function() 
39124     {
39125         
39126         this.preBuild();
39127         var mods = this.buildOrder();
39128       
39129         //this.allmods = mods;
39130         //Roo.debug && Roo.log(mods);
39131         //return;
39132         if (!mods.length) { // should not happen
39133             throw "NO modules!!!";
39134         }
39135         
39136         
39137         var msg = "Building Interface...";
39138         // flash it up as modal - so we store the mask!?
39139         if (!this.hideProgress) {
39140             Roo.MessageBox.show({ title: 'loading' });
39141             Roo.MessageBox.show({
39142                title: "Please wait...",
39143                msg: msg,
39144                width:450,
39145                progress:true,
39146                closable:false,
39147                modal: false
39148               
39149             });
39150         }
39151         var total = mods.length;
39152         
39153         var _this = this;
39154         var progressRun = function() {
39155             if (!mods.length) {
39156                 Roo.debug && Roo.log('hide?');
39157                 if (!this.hideProgress) {
39158                     Roo.MessageBox.hide();
39159                 }
39160                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
39161                 
39162                 // THE END...
39163                 return false;   
39164             }
39165             
39166             var m = mods.shift();
39167             
39168             
39169             Roo.debug && Roo.log(m);
39170             // not sure if this is supported any more.. - modules that are are just function
39171             if (typeof(m) == 'function') { 
39172                 m.call(this);
39173                 return progressRun.defer(10, _this);
39174             } 
39175             
39176             
39177             msg = "Building Interface " + (total  - mods.length) + 
39178                     " of " + total + 
39179                     (m.name ? (' - ' + m.name) : '');
39180                         Roo.debug && Roo.log(msg);
39181             if (!this.hideProgress) { 
39182                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
39183             }
39184             
39185          
39186             // is the module disabled?
39187             var disabled = (typeof(m.disabled) == 'function') ?
39188                 m.disabled.call(m.module.disabled) : m.disabled;    
39189             
39190             
39191             if (disabled) {
39192                 return progressRun(); // we do not update the display!
39193             }
39194             
39195             // now build 
39196             
39197                         
39198                         
39199             m.render();
39200             // it's 10 on top level, and 1 on others??? why...
39201             return progressRun.defer(10, _this);
39202              
39203         }
39204         progressRun.defer(1, _this);
39205      
39206         
39207         
39208     },
39209         
39210         
39211         /**
39212          * Event Object.
39213          *
39214          *
39215          */
39216         event: false, 
39217     /**
39218          * wrapper for event.on - aliased later..  
39219          * Typically use to register a event handler for register:
39220          *
39221          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
39222          *
39223          */
39224     on : false
39225    
39226     
39227     
39228 });
39229
39230 Roo.XComponent.event = new Roo.util.Observable({
39231                 events : { 
39232                         /**
39233                          * @event register
39234                          * Fires when an Component is registered,
39235                          * set the disable property on the Component to stop registration.
39236                          * @param {Roo.XComponent} c the component being registerd.
39237                          * 
39238                          */
39239                         'register' : true,
39240             /**
39241                          * @event beforebuild
39242                          * Fires before each Component is built
39243                          * can be used to apply permissions.
39244                          * @param {Roo.XComponent} c the component being registerd.
39245                          * 
39246                          */
39247                         'beforebuild' : true,
39248                         /**
39249                          * @event buildcomplete
39250                          * Fires on the top level element when all elements have been built
39251                          * @param {Roo.XComponent} the top level component.
39252                          */
39253                         'buildcomplete' : true
39254                         
39255                 }
39256 });
39257
39258 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
39259  //<script type="text/javascript">
39260
39261
39262 /**
39263  * @class Roo.Login
39264  * @extends Roo.LayoutDialog
39265  * A generic Login Dialog..... - only one needed in theory!?!?
39266  *
39267  * Fires XComponent builder on success...
39268  * 
39269  * Sends 
39270  *    username,password, lang = for login actions.
39271  *    check = 1 for periodic checking that sesion is valid.
39272  *    passwordRequest = email request password
39273  *    logout = 1 = to logout
39274  * 
39275  * Affects: (this id="????" elements)
39276  *   loading  (removed) (used to indicate application is loading)
39277  *   loading-mask (hides) (used to hide application when it's building loading)
39278  *   
39279  * 
39280  * Usage: 
39281  *    
39282  * 
39283  * Myapp.login = Roo.Login({
39284      url: xxxx,
39285    
39286      realm : 'Myapp', 
39287      
39288      
39289      method : 'POST',
39290      
39291      
39292      * 
39293  })
39294  * 
39295  * 
39296  * 
39297  **/
39298  
39299 Roo.Login = function(cfg)
39300 {
39301     this.addEvents({
39302         'refreshed' : true
39303     });
39304     
39305     Roo.apply(this,cfg);
39306     
39307     Roo.onReady(function() {
39308         this.onLoad();
39309     }, this);
39310     // call parent..
39311     
39312    
39313     Roo.Login.superclass.constructor.call(this, this);
39314     //this.addxtype(this.items[0]);
39315     
39316     
39317 }
39318
39319
39320 Roo.extend(Roo.Login, Roo.LayoutDialog, {
39321     
39322     /**
39323      * @cfg {String} method
39324      * Method used to query for login details.
39325      */
39326     
39327     method : 'POST',
39328     /**
39329      * @cfg {String} url
39330      * URL to query login data. - eg. baseURL + '/Login.php'
39331      */
39332     url : '',
39333     
39334     /**
39335      * @property user
39336      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
39337      * @type {Object} 
39338      */
39339     user : false,
39340     /**
39341      * @property checkFails
39342      * Number of times we have attempted to get authentication check, and failed.
39343      * @type {Number} 
39344      */
39345     checkFails : 0,
39346       /**
39347      * @property intervalID
39348      * The window interval that does the constant login checking.
39349      * @type {Number} 
39350      */
39351     intervalID : 0,
39352     
39353     
39354     onLoad : function() // called on page load...
39355     {
39356         // load 
39357          
39358         if (Roo.get('loading')) { // clear any loading indicator..
39359             Roo.get('loading').remove();
39360         }
39361         
39362         //this.switchLang('en'); // set the language to english..
39363        
39364         this.check({
39365             success:  function(response, opts)  {  // check successfull...
39366             
39367                 var res = this.processResponse(response);
39368                 this.checkFails =0;
39369                 if (!res.success) { // error!
39370                     this.checkFails = 5;
39371                     //console.log('call failure');
39372                     return this.failure(response,opts);
39373                 }
39374                 
39375                 if (!res.data.id) { // id=0 == login failure.
39376                     return this.show();
39377                 }
39378                 
39379                               
39380                         //console.log(success);
39381                 this.fillAuth(res.data);   
39382                 this.checkFails =0;
39383                 Roo.XComponent.build();
39384             },
39385             failure : this.show
39386         });
39387         
39388     }, 
39389     
39390     
39391     check: function(cfg) // called every so often to refresh cookie etc..
39392     {
39393         if (cfg.again) { // could be undefined..
39394             this.checkFails++;
39395         } else {
39396             this.checkFails = 0;
39397         }
39398         var _this = this;
39399         if (this.sending) {
39400             if ( this.checkFails > 4) {
39401                 Roo.MessageBox.alert("Error",  
39402                     "Error getting authentication status. - try reloading, or wait a while", function() {
39403                         _this.sending = false;
39404                     }); 
39405                 return;
39406             }
39407             cfg.again = true;
39408             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
39409             return;
39410         }
39411         this.sending = true;
39412         
39413         Roo.Ajax.request({  
39414             url: this.url,
39415             params: {
39416                 getAuthUser: true
39417             },  
39418             method: this.method,
39419             success:  cfg.success || this.success,
39420             failure : cfg.failure || this.failure,
39421             scope : this,
39422             callCfg : cfg
39423               
39424         });  
39425     }, 
39426     
39427     
39428     logout: function()
39429     {
39430         window.onbeforeunload = function() { }; // false does not work for IE..
39431         this.user = false;
39432         var _this = this;
39433         
39434         Roo.Ajax.request({  
39435             url: this.url,
39436             params: {
39437                 logout: 1
39438             },  
39439             method: 'GET',
39440             failure : function() {
39441                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
39442                     document.location = document.location.toString() + '?ts=' + Math.random();
39443                 });
39444                 
39445             },
39446             success : function() {
39447                 _this.user = false;
39448                 this.checkFails =0;
39449                 // fixme..
39450                 document.location = document.location.toString() + '?ts=' + Math.random();
39451             }
39452               
39453               
39454         }); 
39455     },
39456     
39457     processResponse : function (response)
39458     {
39459         var res = '';
39460         try {
39461             res = Roo.decode(response.responseText);
39462             // oops...
39463             if (typeof(res) != 'object') {
39464                 res = { success : false, errorMsg : res, errors : true };
39465             }
39466             if (typeof(res.success) == 'undefined') {
39467                 res.success = false;
39468             }
39469             
39470         } catch(e) {
39471             res = { success : false,  errorMsg : response.responseText, errors : true };
39472         }
39473         return res;
39474     },
39475     
39476     success : function(response, opts)  // check successfull...
39477     {  
39478         this.sending = false;
39479         var res = this.processResponse(response);
39480         if (!res.success) {
39481             return this.failure(response, opts);
39482         }
39483         if (!res.data || !res.data.id) {
39484             return this.failure(response,opts);
39485         }
39486         //console.log(res);
39487         this.fillAuth(res.data);
39488         
39489         this.checkFails =0;
39490         
39491     },
39492     
39493     
39494     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
39495     {
39496         this.authUser = -1;
39497         this.sending = false;
39498         var res = this.processResponse(response);
39499         //console.log(res);
39500         if ( this.checkFails > 2) {
39501         
39502             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
39503                 "Error getting authentication status. - try reloading"); 
39504             return;
39505         }
39506         opts.callCfg.again = true;
39507         this.check.defer(1000, this, [ opts.callCfg ]);
39508         return;  
39509     },
39510     
39511     
39512     
39513     fillAuth: function(au) {
39514         this.startAuthCheck();
39515         this.authUserId = au.id;
39516         this.authUser = au;
39517         this.lastChecked = new Date();
39518         this.fireEvent('refreshed', au);
39519         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
39520         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
39521         au.lang = au.lang || 'en';
39522         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
39523         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
39524         this.switchLang(au.lang );
39525         
39526      
39527         // open system... - -on setyp..
39528         if (this.authUserId  < 0) {
39529             Roo.MessageBox.alert("Warning", 
39530                 "This is an open system - please set up a admin user with a password.");  
39531         }
39532          
39533         //Pman.onload(); // which should do nothing if it's a re-auth result...
39534         
39535              
39536     },
39537     
39538     startAuthCheck : function() // starter for timeout checking..
39539     {
39540         if (this.intervalID) { // timer already in place...
39541             return false;
39542         }
39543         var _this = this;
39544         this.intervalID =  window.setInterval(function() {
39545               _this.check(false);
39546             }, 120000); // every 120 secs = 2mins..
39547         
39548         
39549     },
39550          
39551     
39552     switchLang : function (lang) 
39553     {
39554         _T = typeof(_T) == 'undefined' ? false : _T;
39555           if (!_T || !lang.length) {
39556             return;
39557         }
39558         
39559         if (!_T && lang != 'en') {
39560             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
39561             return;
39562         }
39563         
39564         if (typeof(_T.en) == 'undefined') {
39565             _T.en = {};
39566             Roo.apply(_T.en, _T);
39567         }
39568         
39569         if (typeof(_T[lang]) == 'undefined') {
39570             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
39571             return;
39572         }
39573         
39574         
39575         Roo.apply(_T, _T[lang]);
39576         // just need to set the text values for everything...
39577         var _this = this;
39578         /* this will not work ...
39579         if (this.form) { 
39580             
39581                
39582             function formLabel(name, val) {
39583                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
39584             }
39585             
39586             formLabel('password', "Password"+':');
39587             formLabel('username', "Email Address"+':');
39588             formLabel('lang', "Language"+':');
39589             this.dialog.setTitle("Login");
39590             this.dialog.buttons[0].setText("Forgot Password");
39591             this.dialog.buttons[1].setText("Login");
39592         }
39593         */
39594         
39595         
39596     },
39597     
39598     
39599     title: "Login",
39600     modal: true,
39601     width:  350,
39602     //height: 230,
39603     height: 180,
39604     shadow: true,
39605     minWidth:200,
39606     minHeight:180,
39607     //proxyDrag: true,
39608     closable: false,
39609     draggable: false,
39610     collapsible: false,
39611     resizable: false,
39612     center: {  // needed??
39613         autoScroll:false,
39614         titlebar: false,
39615        // tabPosition: 'top',
39616         hideTabs: true,
39617         closeOnTab: true,
39618         alwaysShowTabs: false
39619     } ,
39620     listeners : {
39621         
39622         show  : function(dlg)
39623         {
39624             //console.log(this);
39625             this.form = this.layout.getRegion('center').activePanel.form;
39626             this.form.dialog = dlg;
39627             this.buttons[0].form = this.form;
39628             this.buttons[0].dialog = dlg;
39629             this.buttons[1].form = this.form;
39630             this.buttons[1].dialog = dlg;
39631            
39632            //this.resizeToLogo.defer(1000,this);
39633             // this is all related to resizing for logos..
39634             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
39635            //// if (!sz) {
39636              //   this.resizeToLogo.defer(1000,this);
39637              //   return;
39638            // }
39639             //var w = Ext.lib.Dom.getViewWidth() - 100;
39640             //var h = Ext.lib.Dom.getViewHeight() - 100;
39641             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
39642             //this.center();
39643             if (this.disabled) {
39644                 this.hide();
39645                 return;
39646             }
39647             
39648             if (this.user.id < 0) { // used for inital setup situations.
39649                 return;
39650             }
39651             
39652             if (this.intervalID) {
39653                 // remove the timer
39654                 window.clearInterval(this.intervalID);
39655                 this.intervalID = false;
39656             }
39657             
39658             
39659             if (Roo.get('loading')) {
39660                 Roo.get('loading').remove();
39661             }
39662             if (Roo.get('loading-mask')) {
39663                 Roo.get('loading-mask').hide();
39664             }
39665             
39666             //incomming._node = tnode;
39667             this.form.reset();
39668             //this.dialog.modal = !modal;
39669             //this.dialog.show();
39670             this.el.unmask(); 
39671             
39672             
39673             this.form.setValues({
39674                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
39675                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
39676             });
39677             
39678             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
39679             if (this.form.findField('username').getValue().length > 0 ){
39680                 this.form.findField('password').focus();
39681             } else {
39682                this.form.findField('username').focus();
39683             }
39684     
39685         }
39686     },
39687     items : [
39688          {
39689        
39690             xtype : 'ContentPanel',
39691             xns : Roo,
39692             region: 'center',
39693             fitToFrame : true,
39694             
39695             items : [
39696     
39697                 {
39698                
39699                     xtype : 'Form',
39700                     xns : Roo.form,
39701                     labelWidth: 100,
39702                     style : 'margin: 10px;',
39703                     
39704                     listeners : {
39705                         actionfailed : function(f, act) {
39706                             // form can return { errors: .... }
39707                                 
39708                             //act.result.errors // invalid form element list...
39709                             //act.result.errorMsg// invalid form element list...
39710                             
39711                             this.dialog.el.unmask();
39712                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
39713                                         "Login failed - communication error - try again.");
39714                                       
39715                         },
39716                         actioncomplete: function(re, act) {
39717                              
39718                             Roo.state.Manager.set(
39719                                 this.dialog.realm + '.username',  
39720                                     this.findField('username').getValue()
39721                             );
39722                             Roo.state.Manager.set(
39723                                 this.dialog.realm + '.lang',  
39724                                 this.findField('lang').getValue() 
39725                             );
39726                             
39727                             this.dialog.fillAuth(act.result.data);
39728                               
39729                             this.dialog.hide();
39730                             
39731                             if (Roo.get('loading-mask')) {
39732                                 Roo.get('loading-mask').show();
39733                             }
39734                             Roo.XComponent.build();
39735                             
39736                              
39737                             
39738                         }
39739                     },
39740                     items : [
39741                         {
39742                             xtype : 'TextField',
39743                             xns : Roo.form,
39744                             fieldLabel: "Email Address",
39745                             name: 'username',
39746                             width:200,
39747                             autoCreate : {tag: "input", type: "text", size: "20"}
39748                         },
39749                         {
39750                             xtype : 'TextField',
39751                             xns : Roo.form,
39752                             fieldLabel: "Password",
39753                             inputType: 'password',
39754                             name: 'password',
39755                             width:200,
39756                             autoCreate : {tag: "input", type: "text", size: "20"},
39757                             listeners : {
39758                                 specialkey : function(e,ev) {
39759                                     if (ev.keyCode == 13) {
39760                                         this.form.dialog.el.mask("Logging in");
39761                                         this.form.doAction('submit', {
39762                                             url: this.form.dialog.url,
39763                                             method: this.form.dialog.method
39764                                         });
39765                                     }
39766                                 }
39767                             }  
39768                         },
39769                         {
39770                             xtype : 'ComboBox',
39771                             xns : Roo.form,
39772                             fieldLabel: "Language",
39773                             name : 'langdisp',
39774                             store: {
39775                                 xtype : 'SimpleStore',
39776                                 fields: ['lang', 'ldisp'],
39777                                 data : [
39778                                     [ 'en', 'English' ],
39779                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
39780                                     [ 'zh_CN', '\u7C21\u4E2D' ]
39781                                 ]
39782                             },
39783                             
39784                             valueField : 'lang',
39785                             hiddenName:  'lang',
39786                             width: 200,
39787                             displayField:'ldisp',
39788                             typeAhead: false,
39789                             editable: false,
39790                             mode: 'local',
39791                             triggerAction: 'all',
39792                             emptyText:'Select a Language...',
39793                             selectOnFocus:true,
39794                             listeners : {
39795                                 select :  function(cb, rec, ix) {
39796                                     this.form.switchLang(rec.data.lang);
39797                                 }
39798                             }
39799                         
39800                         }
39801                     ]
39802                 }
39803                   
39804                 
39805             ]
39806         }
39807     ],
39808     buttons : [
39809         {
39810             xtype : 'Button',
39811             xns : 'Roo',
39812             text : "Forgot Password",
39813             listeners : {
39814                 click : function() {
39815                     //console.log(this);
39816                     var n = this.form.findField('username').getValue();
39817                     if (!n.length) {
39818                         Roo.MessageBox.alert("Error", "Fill in your email address");
39819                         return;
39820                     }
39821                     Roo.Ajax.request({
39822                         url: this.dialog.url,
39823                         params: {
39824                             passwordRequest: n
39825                         },
39826                         method: this.dialog.method,
39827                         success:  function(response, opts)  {  // check successfull...
39828                         
39829                             var res = this.dialog.processResponse(response);
39830                             if (!res.success) { // error!
39831                                Roo.MessageBox.alert("Error" ,
39832                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
39833                                return;
39834                             }
39835                             Roo.MessageBox.alert("Notice" ,
39836                                 "Please check you email for the Password Reset message");
39837                         },
39838                         failure : function() {
39839                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
39840                         }
39841                         
39842                     });
39843                 }
39844             }
39845         },
39846         {
39847             xtype : 'Button',
39848             xns : 'Roo',
39849             text : "Login",
39850             listeners : {
39851                 
39852                 click : function () {
39853                         
39854                     this.dialog.el.mask("Logging in");
39855                     this.form.doAction('submit', {
39856                             url: this.dialog.url,
39857                             method: this.dialog.method
39858                     });
39859                 }
39860             }
39861         }
39862     ]
39863   
39864   
39865 })
39866  
39867
39868
39869